if (FALSE) {
installPackagesNeeded()
}
Processo
Um paciente que está em lista de espera para ser operado:
- Entra em contacto com o serviço de cirurgia, vários dias antes da
sua operação,
- Através de uma chamada telefónica ou
- Através do envio de uma mensagem escrita numa app.
- Os serviços administrativos atendem as chamadas ou lêem a mensagem
via app e, mediante
- a informação presente no sistema dada pelo médico assistente e
- a informação fornecida pelo paciente, o administrativo decide se
agenda
- uma consulta presencial antes da cirurgia, ou
- uma consulta telefónica, ou
- não há necessidade de qualquer consulta.
Contudo, este procedimento necessita de melhorias. O objectivo deste
estudo é optimizar a gestão dos contactos (tempo total do processo e
total de chamadas perdidas)
Situação atual
Apenas 2 administrativos no serviço, ambos começam às 8h e acabam
às 18h (10h de trabalho) de cada dia útil.
O tempo entre chegadas tem exp(1/40) (pq media de 40/h)
Tipo de contacto:
- prob de telefone = 0.85
- prob de app = 0.15
- Tipo de consulta:
- 0.3 não é agendado consulta,
- 0.6 consula por telefone,
- 0.1 presencial
- Tempo de
- Conversa antes da decisao é norm(4,1^2)
- Tomada de decisao do adminstrativo é norm(1,0.25^2)
- Tempo de agendamento SE NECESSÁRIO é norm(1,0.25^2)
- 5o telefone desliga apos 5 minutos de espera (e fica chamada
perdida)
library(simmer)
library(simmer.bricks)
library(simmer.plot)
conflicts_prefer(simmer::rollback)
[conflicted] Removing existing preference.[conflicted] Will prefer simmer::rollback over any other package.
conflicts_prefer(simmer::now)
[conflicted] Removing existing preference.[conflicted] Will prefer simmer::now over any other package.
conflicts_prefer(dplyr::select)
[conflicted] Removing existing preference.[conflicted] Will prefer dplyr::select over any other package.
# this will be substituted below and literally just is here because simmer doesn't know how to implement pipes and I hate it here
env <- simmer("hospital")
Resources: Server: O hospital com 2 administradores Queue: Fila de
espera com prioridade de clients em chamada Não há vantgem em escolher a
app quando há chamada de espera Manager: vai atribuir o tipo de consulta
(e Vai alterar o tipo de cliente se ele for rejeitado na chamada?)
Gerador: vai gerir novos arrivals (exp(1/40)) arrival: cada paciente
(que leva uma trajectory) trajectory: a receita de cada arrival
# atributes need to be numeric
tipo_de_consulta.PRESENCIAL <- 1
tipo_de_consulta.TELEFONICA <- 2
tipo_de_consulta.NAO_AGENDADA <- 3
tipos_de_consulta <- c(tipo_de_consulta.PRESENCIAL, tipo_de_consulta.TELEFONICA, tipo_de_consulta.NAO_AGENDADA)
tipos_de_consulta.probs <- c(0.1, 0.6, 0.3)
tipo_de_contacto.TELEFONE <- 1
tipo_de_contacto.APP <- 2
tipos_de_contacto <- c(tipo_de_contacto.TELEFONE, tipo_de_contacto.APP)
tipos_de_contacto.probs <- c(0.85, 0.15)
tempo_atendido <- function() {
mean_time_triagem <- rnorm(1, mean = 4, sd = 1)
mean_time_decision <- rnorm(1, mean = 1, sd = 0.25)
time_to_timeout <- mean_time_triagem + mean_time_decision
if (get_attribute(env, "tipo_de_consulta") != tipo_de_consulta.NAO_AGENDADA) {
time_to_timeout <- time_to_timeout + rnorm(1, mean = 1, sd = 0.25)
}
return(time_to_timeout)
}
intime <- function() {
now(env)%% 24*60 -> n
return(8*60 < n & n < 18*60)
}
pessoa <- trajectory("pessoa") %>%
# set_attribute("times_phone_rejected", 0) %>%
# set_attribute("phone_rejected", 0) %>%
set_attribute("tipo_de_consulta", \() sample(tipos_de_consulta, 1, prob = tipos_de_consulta.probs)) %>%
set_attribute("tipo_de_contacto", \() sample(tipos_de_contacto, 1, prob = tipos_de_contacto.probs), tag = "contacto") %>%
set_prioritization(\() if (get_attribute(env, "tipo_de_contacto") == tipo_de_contacto.TELEFONE) c(1, -1, FALSE) else c(0 , -1, FALSE)) %>% # PRIORITY, DROP PRIORITY, RESTART
# log_(\() paste("Vou pra fila agora, estou a usar", ifelse(get_attribute(env, "tipo_de_contacto") == tipo_de_contacto.TELEFONE, "o telefone", "a app"))) %>%
renege_in(\() ifelse(get_attribute(env, "tipo_de_contacto") == tipo_de_contacto.TELEFONE, 5, Inf), out =
trajectory() %>% set_attribute("phone_rejected", 1)
# O botas não gostou
# trajectory("pessoa_try_again") %>%
# set_attribute("times_phone_rejected", \() get_attribute(env, "times_phone_rejected") + 1) %>%
# # leave if out of schedule
# leave(\() {ifelse(intime(), 0, 1)}) %>%
# # log_(\() "Nao fui atendido, vou voltar pra fila") %>%
# rollback(target="contacto", times = 1) # não funciona, mudar pra numero?
) %>%
seize("administrador", 1) %>%
renege_abort() %>%
# log_(\() paste("Before timeout")) %>%
set_attribute("atendido", 1) %>%
timeout(tempo_atendido) %>%
# log_(\() "After timeout") %>%
release("administrador", 1)
# log_(\() "Leaving")
pessoa
trajectory: pessoa, 10 activities
{ Activity: SetAttribute | keys: [tipo_de_consulta], values: function(), global: 0, mod: N, init: 0 }
{ Activity: SetAttribute | [contacto] keys: [tipo_de_contacto], values: function(), global: 0, mod: N, init: 0 }
{ Activity: SetPrior | values: function(), mod: N }
{ Activity: RenegeIn | t: function(), keep_seized: 0 }
Fork 1, stop, trajectory: anonymous, 1 activities
{ Activity: SetAttribute | keys: [phone_rejected], values: [1], global: 0, mod: N, init: 0 }
{ Activity: Seize | resource: administrador, amount: 1 }
{ Activity: RenegeAbort | }
{ Activity: SetAttribute | keys: [atendido], values: [1], global: 0, mod: N, init: 0 }
{ Activity: Timeout | delay: function() }
{ Activity: Release | resource: administrador, amount: 1 }
set.seed(1)
# 50 replicas
envs <- lapply(1:50, function(i) {
env <<- simmer("hospital")
env %>%
add_generator("pessoa", pessoa, from_to(8*60, 18*60, \() rexp(1, 40/60), every = 24*60, arrive = F), mon = 2) %>%
add_resource("administrador", schedule(c(8*60, 18*60), c(2, 0), period = 24*60)) %>%
run(24*60*5)
})
envs %>% simmer.plot::get_mon_arrivals(ongoing = T) -> arrivals
envs %>% simmer.plot::get_mon_attributes() -> attributes
envs %>% simmer.plot::get_mon_resources() -> resources
arrivals %>%
filter(start_time != -1) %>% # n sei pq e q isto acontece
arrange(start_time) %>%
select(-activity_time) %>%
mutate(
day = ceiling(start_time/(24*60)),
start_time_day = start_time %% (24*60) %>% seconds_to_period(),
end_time_day = end_time %% (24*60) %>% seconds_to_period(),
# replication = replication %>% as_factor(),
) -> arrivals.df1
arrivals.df1 %>% filter(replication == 1) %>% mutate(start_time_day = start_time_day %>% round(3), end_time_day = end_time_day %>% round(3))
# turn what's above into a function so it can be done for 2a and 2b
# get_arrivals <- function(mon_attributes) {
# mon_attributes %>%
# filter(start_time != -1) %>% # n sei pq e q isto acontece
# arrange(start_time) %>%
# select(-activity_time) %>%
# mutate(
# day = ceiling(start_time/(24*60)),
# start_time_day = start_time %% (24*60) %>% seconds_to_period(),
# end_time_day = end_time %% (24*60) %>% seconds_to_period(),
# # replication = replication %>% as_factor(),
# )
# }
#
# pprint_arrivals <- function (arrivals.df1) {
# arrivals.df1 %>% filter(replication == 1) %>% mutate(start_time_day = start_time_day %>% round(3), end_time_day = # end_time_day %>% round(3))
# }
# analise de eficiencia
# numero de chamadas perdidas
# add day to atributes from arrivals.df1
attributes %>%
left_join(arrivals.df1 %>% select(name, replication, day, start_time, end_time, end_time_day), by = c("name", "replication")) %>%
rename(atribute_time = time, pessoa_start_time = start_time, pessoa_end_time = end_time) %>%
select(atribute_time, name, key, value, replication, day, pessoa_start_time, pessoa_end_time) -> attributes.df1
attributes.df1
# pessoas e os seus tipos de chamada
attributes.df1 %>%
filter(key == "tipo_de_contacto") %>%
select(name, replication, value) %>%
mutate(value = ifelse(value == tipo_de_contacto.TELEFONE, "telefone", "app")) %>%
rename(contacto = value) -> contactos.df1
# percentagem de chamadas perdidas
attributes.df1 %>%
filter(key == "tipo_de_contacto", value == tipo_de_contacto.TELEFONE) %>%
left_join(attributes.df1 %>% filter(key == "phone_rejected") %>% mutate(gave_up = key) %>% select(name, replication, gave_up), join_by("name", "replication")) %>%
mutate(gave_up = if_else(gave_up %>% is.na, T, F), replication = replication %>% as_factor) %>%
select(name, replication, pessoa_end_time, gave_up) -> phone_calls
phone_calls
# phone_calls rate per replication
phone_calls %>%
group_by(replication) %>%
summarise(phone_calls = n(), gave_up = sum(gave_up)) %>%
mutate(phone_calls_per_hour = phone_calls/10, gave_up_rate = gave_up/phone_calls) %>%
arrange(desc(gave_up_rate)) -> phone_calls_rate
phone_calls_rate
phone_calls_rate %>%
ggplot(aes(x = reorder(replication, gave_up_rate), y = gave_up_rate)) +
geom_col() +
ylim(0, 1) +
theme(axis.text.x = element_text(angle = 70, hjust = 1)) +
geom_hline(aes(yintercept = min(gave_up_rate)), linetype = "dashed", color = "red") +
geom_hline(aes(yintercept = max(gave_up_rate)), linetype = "dashed", color = "blue") +
xlab("Replication") +
ylab("Gave up rate")

phone_calls_rate %>%
summarise(
min = min(phone_calls_per_hour),
mean = mean(phone_calls_per_hour),
max = max(phone_calls_per_hour)
)
# evolucao ao longo do dia DO QUE?
attributes.df1 %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none")

# get queue size histogram
resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()) %>%
ggplot(aes(x = time, y = queue_size)) +
geom_step(aes(color = replication), alpha = 0.3) +
geom_smooth(method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none")

resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()) %>%
ggplot(aes(x = time, y = queue_size)) +
geom_step(aes(color = replication), alpha = 0.3) +
geom_smooth(method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
ylim(0,250)

NA
NA
# join these 2 plots
attributes.df1 %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
geom_step(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), alpha = 0.3) +
geom_smooth(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
ylim(0,875)

attributes.df1 %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
geom_step(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), alpha = 0.3) +
geom_smooth(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none")

attributes.df1 %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
theme(legend.position = "none") +
geom_step(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), alpha = 0.3) +
geom_smooth(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), method="lm") +
theme(legend.position = "none")

# tempo de espera daqueles que concluiram, speardao por chamada e mensagem
attributes %>%
filter(key == "atendido") %>%
left_join(arrivals.df1 %>% select(start_time, name, replication), join_by("name", "replication")) %>%
left_join(contactos.df1, join_by("name", "replication")) %>%
mutate(time_waiting = time - start_time) %>%
select(name, value, replication, time_waiting, contacto) -> waiting_times
waiting_times
# histograma (n tou a perceber pq é q há waiting times maiores que 5)
waiting_times %>%
filter(contacto == "telefone") %>%
ggplot(aes(x = time_waiting)) +
geom_histogram(bins = 100) +
theme(legend.position = "none")

waiting_times %>%
filter(contacto == "app") %>%
ggplot(aes(x = time_waiting/60)) +
geom_histogram(bins = 100) +
theme(legend.position = "none")

# regular waiting time stats without graphs
waiting_times %>%
group_by(contacto) %>%
summarise(mean = mean(time_waiting), sd = sd(time_waiting), median = median(time_waiting))
waiting_times %>%
group_by(contacto) %>%
summarise(quantile_10 = quantile(time_waiting, 0.1), quantile_20 = quantile(time_waiting, 0.2), quantile_30 = quantile(time_waiting, 0.3), quantile_40 = quantile(time_waiting, 0.4), quantile_50 = quantile(time_waiting, 0.5), quantile_60 = quantile(time_waiting, 0.6), quantile_70 = quantile(time_waiting, 0.7), quantile_80 = quantile(time_waiting, 0.8), quantile_90 = quantile(time_waiting, 0.9), quantile_95 = quantile(time_waiting, 0.95), quantile_99 = quantile(time_waiting, 0.99))
resources %>% plot(metric = "utilization")

resources %>% plot(metric = "usage", steps = T)

Pergunta 2
Alíneas com mudanças relativas às interrupções
# mudanças
envs <- lapply(1:50, function(i) {
env <<- simmer("hospital")
env %>%
add_generator("pessoa_de_manha", pessoa, from_to(8*60, 10*60, \() rexp(1, (120/2)/60), every = 24*60, arrive = F), mon = 2) %>%
add_generator("pessoa_de_tarde", pessoa, from_to(10*60, 16*60, \() rexp(1, (240/6)/60), every = 24*60, arrive = F), mon = 2) %>%
add_generator("pessoa_de_noite", pessoa, from_to(16*60, 18*60, \() rexp(1, (40/2)/60), every = 24*60, arrive = F), mon = 2) %>%
add_resource("administrador", schedule(
c(8 , 9 , 9.5 , 12 , 15.5 ,18)*60,
c( 1 , 2, 4, 3, 1, 0),
period = 24*60
)) %>%
run(24*60*5)
})
envs %>% simmer.plot::get_mon_arrivals(ongoing = T) -> arrivals
envs %>% simmer.plot::get_mon_attributes() -> attributes
envs %>% simmer.plot::get_mon_resources() -> resources
2.b)
# b)
arrivals %>%
filter(start_time != -1) %>% # n sei pq e q isto acontece
arrange(start_time) %>%
select(-activity_time) %>%
mutate(
day = ceiling(start_time/(24*60)),
start_time_day = start_time %% (24*60) %>% seconds_to_period(),
end_time_day = end_time %% (24*60) %>% seconds_to_period(),
replication = replication %>% as_factor(),
) -> arrivals.df2b
arrivals.df2b %>% filter(replication == 1) %>% mutate(start_time_day = start_time_day %>% round(3), end_time_day = end_time_day %>% round(3))
arrivals.df2b$replication <- as.integer(as.character(arrivals.df2b$replication))
# tempo de espera daqueles que concluiram, speardao por chamada e mensagem
arrivals.df2b %>%
filter(finished == TRUE) %>%
mutate(waiting_time = end_time - start_time) %>%
left_join(attributes %>% filter(key == "tipo_de_contacto"), by = c("name", "replication")) %>%
mutate(tipo_de_contacto = ifelse(value == tipo_de_contacto.TELEFONE, "telefone", "mensagem") %>% as_factor, replication = replication %>% as_factor) %>%
select(name, replication, waiting_time, tipo_de_contacto) -> waiting_times.2b
waiting_times.2b
# analise de eficiencia
# numero de chamadas perdidas
# add day to atributes from arrivals.df2b
attributes %>%
left_join(arrivals.df2b %>% select(name, replication, day, start_time, end_time, end_time_day), by = c("name", "replication")) %>%
rename(atribute_time = time, pessoa_start_time = start_time, pessoa_end_time = end_time) %>%
select(atribute_time, name, key, value, replication, day, pessoa_start_time, pessoa_end_time) -> attributes.df2b
attributes.df2b
# pessoas e os seus tipos de chamada
attributes.df2b %>%
filter(key == "tipo_de_contacto") %>%
select(name, replication, value) %>%
mutate(value = ifelse(value == tipo_de_contacto.TELEFONE, "telefone", "app")) %>%
rename(contacto = value) -> contactos.df2b
# percentagem de chamadas perdidas
attributes.df2b %>%
filter(key == "tipo_de_contacto", value == tipo_de_contacto.TELEFONE) %>%
left_join(attributes.df2b %>% filter(key == "phone_rejected") %>% mutate(gave_up = key) %>% select(name, replication, gave_up), join_by("name", "replication")) %>%
mutate(gave_up = if_else(gave_up %>% is.na, T, F), replication = replication %>% as_factor) %>%
select(name, replication, pessoa_end_time, gave_up) -> phone_calls
phone_calls
# phone_calls rate per replication
phone_calls %>%
group_by(replication) %>%
summarise(phone_calls = n(), gave_up = sum(gave_up)) %>%
mutate(phone_calls_per_hour = phone_calls/10, gave_up_rate = gave_up/phone_calls) %>%
arrange(desc(gave_up_rate)) -> phone_calls_rate
phone_calls_rate
phone_calls_rate %>%
ggplot(aes(x = reorder(replication, gave_up_rate), y = gave_up_rate)) +
geom_col() +
ylim(0, 1) +
theme(axis.text.x = element_text(angle = 70, hjust = 1)) +
geom_hline(aes(yintercept = min(gave_up_rate)), linetype = "dashed", color = "red") +
geom_hline(aes(yintercept = max(gave_up_rate)), linetype = "dashed", color = "blue") +
xlab("Replication") +
ylab("Gave up rate")

phone_calls_rate %>%
summarise(
min = min(phone_calls_per_hour),
mean = mean(phone_calls_per_hour),
max = max(phone_calls_per_hour)
)
# evolucao ao longo do dia DO QUE?
attributes.df2b %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none")

# get queue size histogram
resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()) %>%
ggplot(aes(x = time, y = queue_size)) +
geom_step(aes(color = replication), alpha = 0.3) +
geom_smooth(method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none")

resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()) %>%
ggplot(aes(x = time, y = queue_size)) +
geom_step(aes(color = replication), alpha = 0.3) +
geom_smooth(method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
ylim(0,250)

NA
# join these 2 plots
attributes.df2b %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
geom_step(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), alpha = 0.3) +
geom_smooth(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
ylim(0,875)

attributes.df2b %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
geom_step(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), alpha = 0.3) +
geom_smooth(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
ggtitle("Tamanho da fila e chamadas rejeitadas 2b) por dia") +
xlab("Tempo de simulação (horas)") +
ylab("Número de chamadas rejeitadas")

attributes.df2b %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
theme(legend.position = "none") +
geom_step(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), alpha = 0.3) +
geom_smooth(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), method="lm") +
theme(legend.position = "none") +
ggtitle("Tamanho da fila e chamadas rejeitadas 2b) nos 5 dias") +
xlab("Tempo de simulação (horas)") +
ylab("Número de chamadas rejeitadas")

# tempo de espera daqueles que concluiram, speardao por chamada e mensagem
attributes %>%
filter(key == "atendido") %>%
left_join(arrivals.df2b %>% select(start_time, name, replication), join_by("name", "replication")) %>%
left_join(contactos.df2b, join_by("name", "replication")) %>%
mutate(time_waiting = time - start_time) %>%
select(name, value, replication, time_waiting, contacto) -> waiting_times
waiting_times
# histograma (n tou a perceber pq é q há waiting times maiores que 5)
waiting_times %>%
filter(contacto == "telefone") %>%
ggplot(aes(x = time_waiting)) +
geom_histogram(bins = 100) +
theme(legend.position = "none")+
ylim(0, 1000)

waiting_times %>%
filter(contacto == "app") %>%
ggplot(aes(x = time_waiting/60)) +
geom_histogram(bins = 100) +
theme(legend.position = "none")

# regular waiting time stats without graphs
waiting_times %>%
group_by(contacto) %>%
summarise(mean = mean(time_waiting), sd = sd(time_waiting), median = median(time_waiting))
waiting_times %>%
group_by(contacto) %>%
summarise(quantile_10 = quantile(time_waiting, 0.1), quantile_20 = quantile(time_waiting, 0.2), quantile_30 = quantile(time_waiting, 0.3), quantile_40 = quantile(time_waiting, 0.4), quantile_50 = quantile(time_waiting, 0.5), quantile_60 = quantile(time_waiting, 0.6), quantile_70 = quantile(time_waiting, 0.7), quantile_80 = quantile(time_waiting, 0.8), quantile_90 = quantile(time_waiting, 0.9), quantile_95 = quantile(time_waiting, 0.95), quantile_99 = quantile(time_waiting, 0.99))
resources %>% plot(metric = "utilization")

resources %>% plot(metric = "usage", steps = T)

2.a) - - - - - - - - - - - -
# a) set_prioritization(\() if (get_attribute(env, "tipo_de_consulta") == tipo_de_consulta.TELEFONICA) c(1, -1, FALSE) else c(0 , -1, FALSE))
pessoa__ <- join(
pessoa[1:2],
trajectory("change_prio") %>%
set_prioritization(\() if (get_attribute(env, "tipo_de_contacto") == tipo_de_contacto.TELEFONE) c(1, 1, FALSE) else c(0 , 0, FALSE)),
pessoa[-(1:3)]
)
pessoa__
trajectory: pessoa, 10 activities
{ Activity: SetAttribute | keys: [tipo_de_consulta], values: function(), global: 0, mod: N, init: 0 }
{ Activity: SetAttribute | [contacto] keys: [tipo_de_contacto], values: function(), global: 0, mod: N, init: 0 }
{ Activity: SetPrior | values: function(), mod: N }
{ Activity: RenegeIn | t: function(), keep_seized: 0 }
Fork 1, stop, trajectory: anonymous, 1 activities
{ Activity: SetAttribute | keys: [phone_rejected], values: [1], global: 0, mod: N, init: 0 }
{ Activity: Seize | resource: administrador, amount: 1 }
{ Activity: RenegeAbort | }
{ Activity: SetAttribute | keys: [atendido], values: [1], global: 0, mod: N, init: 0 }
{ Activity: Timeout | delay: function() }
{ Activity: Release | resource: administrador, amount: 1 }
envs <- lapply(1:50, function(i) {
env <<- simmer("hospital")
env %>%
add_generator("pessoa_de_manha", pessoa__, from_to(8*60, 10*60, \() rexp(1, (120/2)/60), every = 24*60, arrive = F), mon = 2) %>%
add_generator("pessoa_de_tarde", pessoa__, from_to(10*60, 16*60, \() rexp(1, (240/6)/60), every = 24*60, arrive = F), mon = 2) %>%
add_generator("pessoa_de_noite", pessoa__, from_to(16*60, 18*60, \() rexp(1, (40/2)/60), every = 24*60, arrive = F), mon = 2) %>%
add_resource("administrador", schedule(
c(8 , 9 , 9.5 , 12 , 15.5 ,18)*60,
c( 1 , 2, 4, 3, 1, 0),
period = 24*60
)) %>%
run(24*60*5)
})
envs %>% simmer.plot::get_mon_arrivals(ongoing = T) -> arrivals
envs %>% simmer.plot::get_mon_attributes() -> attributes
envs %>% simmer.plot::get_mon_resources() -> resources
arrivals %>%
filter(start_time != -1) %>% # n sei pq e q isto acontece
arrange(start_time) %>%
select(-activity_time) %>%
mutate(
day = ceiling(start_time/(24*60)),
start_time_day = start_time %% (24*60) %>% seconds_to_period(),
end_time_day = end_time %% (24*60) %>% seconds_to_period(),
replication = replication %>% as_factor(),
) -> arrivals.df2a
arrivals.df2a %>% filter(replication == 1) %>% mutate(start_time_day = start_time_day %>% round(3), end_time_day = end_time_day %>% round(3))
arrivals.df2a$replication <- as.integer(as.character(arrivals.df2a$replication))
# analise de eficiencia
attributes %>%
left_join(arrivals.df2a, by = c("name", "replication") ) %>%
select(name, key, value, replication, day, end_time, end_time_day) -> atributes.df2a
atributes.df2a %>%
filter(key == "phone_rejected")
# analise de eficiencia
# numero de chamadas perdidas
# add day to atributes from arrivals.df2a
attributes %>%
left_join(arrivals.df2a %>% select(name, replication, day, start_time, end_time, end_time_day), by = c("name", "replication")) %>%
rename(atribute_time = time, pessoa_start_time = start_time, pessoa_end_time = end_time) %>%
select(atribute_time, name, key, value, replication, day, pessoa_start_time, pessoa_end_time) -> attributes.df2a
attributes.df2a
# pessoas e os seus tipos de chamada
attributes.df2a %>%
filter(key == "tipo_de_contacto") %>%
select(name, replication, value) %>%
mutate(value = ifelse(value == tipo_de_contacto.TELEFONE, "telefone", "app")) %>%
rename(contacto = value) -> contactos.df2a
# percentagem de chamadas perdidas
attributes.df2a %>%
filter(key == "tipo_de_contacto", value == tipo_de_contacto.TELEFONE) %>%
left_join(attributes.df2a %>% filter(key == "phone_rejected") %>% mutate(gave_up = key) %>% select(name, replication, gave_up), join_by("name", "replication")) %>%
mutate(gave_up = if_else(gave_up %>% is.na, T, F), replication = replication %>% as_factor) %>%
select(name, replication, pessoa_end_time, gave_up) -> phone_calls
phone_calls
# phone_calls rate per replication
phone_calls %>%
group_by(replication) %>%
summarise(phone_calls = n(), gave_up = sum(gave_up)) %>%
mutate(phone_calls_per_hour = phone_calls/10, gave_up_rate = gave_up/phone_calls) %>%
arrange(desc(gave_up_rate)) -> phone_calls_rate
phone_calls_rate
phone_calls_rate %>%
ggplot(aes(x = reorder(replication, gave_up_rate), y = gave_up_rate)) +
geom_col() +
ylim(0, 1) +
theme(axis.text.x = element_text(angle = 70, hjust = 1)) +
geom_hline(aes(yintercept = min(gave_up_rate)), linetype = "dashed", color = "red") +
geom_hline(aes(yintercept = max(gave_up_rate)), linetype = "dashed", color = "blue") +
xlab("Replication") +
ylab("Gave up rate")

phone_calls_rate %>%
summarise(
min = min(phone_calls_per_hour),
mean = mean(phone_calls_per_hour),
max = max(phone_calls_per_hour)
)
# evolucao ao longo do dia DO QUE?
attributes.df2a %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none")

# get queue size histogram
resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()) %>%
ggplot(aes(x = time, y = queue_size)) +
geom_step(aes(color = replication), alpha = 0.3) +
geom_smooth(method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none")

resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()) %>%
ggplot(aes(x = time, y = queue_size)) +
geom_step(aes(color = replication), alpha = 0.3) +
geom_smooth(method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
ylim(0,250)

NA
# join these 2 plots
attributes.df2a %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
geom_step(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), alpha = 0.3) +
geom_smooth(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
ylim(0,875)

attributes.df2a %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
geom_step(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), alpha = 0.3) +
geom_smooth(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), method="lm") +
facet_wrap(vars(day), scale = "free") +
theme(legend.position = "none") +
ggtitle("Tamanho da fila e chamadas rejeitadas 2a) por dia") +
xlab("Tempo de simulação (horas)") +
ylab("Número de chamadas rejeitadas")

attributes.df2a %>%
filter(key == "phone_rejected") %>%
mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>%
group_by(replication) %>%
reframe(time_rejected = pessoa_end_time/60, sumcum_per_day = cumsum(value), day = day) %>%
ggplot(aes(x = time_rejected, y = sumcum_per_day)) +
geom_line(aes(color = replication), alpha = 0.3) +
geom_smooth() +
theme(legend.position = "none") +
geom_step(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), alpha = 0.3) +
geom_smooth(data = resources %>%
mutate(day = ceiling(time/(24*60))) %>%
group_by(replication) %>%
reframe(day = day %>% as_factor(), time = time/60, queue_size = queue) %>%
mutate(replication = replication %>% as_factor()),
aes(x = time, y = queue_size), method="lm") +
theme(legend.position = "none") +
ggtitle("Tamanho da fila e chamadas rejeitadas 2a) nos 5 dias") +
xlab("Tempo de simulação (horas)") +
ylab("Número de chamadas rejeitadas")

# tempo de espera daqueles que concluiram, speardao por chamada e mensagem
attributes %>%
filter(key == "atendido") %>%
left_join(arrivals.df2a %>% select(start_time, name, replication), join_by("name", "replication")) %>%
left_join(contactos.df2a, join_by("name", "replication")) %>%
mutate(time_waiting = time - start_time) %>%
select(name, value, replication, time_waiting, contacto) -> waiting_times
waiting_times
# histograma (n tou a perceber pq é q há waiting times maiores que 5)
waiting_times %>%
filter(contacto == "telefone") %>%
ggplot(aes(x = time_waiting)) +
geom_histogram(bins = 100) +
theme(legend.position = "none")+
ylim(0, 1000)

waiting_times %>%
filter(contacto == "app") %>%
ggplot(aes(x = time_waiting/60)) +
geom_histogram(bins = 100) +
theme(legend.position = "none")

# regular waiting time stats without graphs
waiting_times %>%
group_by(contacto) %>%
summarise(mean = mean(time_waiting), sd = sd(time_waiting), median = median(time_waiting))
waiting_times %>%
group_by(contacto) %>%
summarise(quantile_10 = quantile(time_waiting, 0.1), quantile_20 = quantile(time_waiting, 0.2), quantile_30 = quantile(time_waiting, 0.3), quantile_40 = quantile(time_waiting, 0.4), quantile_50 = quantile(time_waiting, 0.5), quantile_60 = quantile(time_waiting, 0.6), quantile_70 = quantile(time_waiting, 0.7), quantile_80 = quantile(time_waiting, 0.8), quantile_90 = quantile(time_waiting, 0.9), quantile_95 = quantile(time_waiting, 0.95), quantile_99 = quantile(time_waiting, 0.99))
resources %>% plot(metric = "utilization")

resources %>% plot(metric = "usage", steps = T)

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCmlmIChGQUxTRSkgew0KICBpbnN0YWxsUGFja2FnZXNOZWVkZWQoKQ0KfQ0KYGBgDQoNCg0KIyMgUHJvY2Vzc28NCg0KVW0gcGFjaWVudGUgcXVlIGVzdMOhIGVtIGxpc3RhIGRlIGVzcGVyYSBwYXJhIHNlciBvcGVyYWRvOg0KDQoxLiBFbnRyYSBlbSBjb250YWN0byBjb20gbyBzZXJ2acOnbyBkZSBjaXJ1cmdpYSwgdsOhcmlvcyBkaWFzIGFudGVzIGRhIHN1YSBvcGVyYcOnw6NvLCANCiAgKiBBdHJhdsOpcyBkZSB1bWEgY2hhbWFkYSB0ZWxlZsOzbmljYSBvdSANCiAgKiBBdHJhdsOpcyBkbyBlbnZpbyBkZSB1bWEgbWVuc2FnZW0gZXNjcml0YSBudW1hIGFwcC4NCg0KMi4gT3Mgc2VydmnDp29zIGFkbWluaXN0cmF0aXZvcyBhdGVuZGVtIGFzIGNoYW1hZGFzIG91IGzDqmVtIGEgbWVuc2FnZW0gdmlhIGFwcCBlLCBtZWRpYW50ZQ0KICAqIGEgaW5mb3JtYcOnw6NvIHByZXNlbnRlIG5vIHNpc3RlbWEgZGFkYSBwZWxvIG3DqWRpY28gYXNzaXN0ZW50ZSBlIA0KICAqIGEgaW5mb3JtYcOnw6NvIGZvcm5lY2lkYSBwZWxvIHBhY2llbnRlLCANCm8gYWRtaW5pc3RyYXRpdm8gZGVjaWRlIHNlIGFnZW5kYQ0KICAqIHVtYSBjb25zdWx0YSBwcmVzZW5jaWFsIGFudGVzIGRhIGNpcnVyZ2lhLCBvdQ0KICAqIHVtYSBjb25zdWx0YSB0ZWxlZsOzbmljYSwgb3UNCiAgKiBuw6NvIGjDoSBuZWNlc3NpZGFkZSBkZSBxdWFscXVlciBjb25zdWx0YS4gDQoNCkNvbnR1ZG8sIGVzdGUgcHJvY2VkaW1lbnRvIG5lY2Vzc2l0YSBkZSBtZWxob3JpYXMuIA0KTyBvYmplY3Rpdm8gZGVzdGUgZXN0dWRvIMOpIG9wdGltaXphciBhIGdlc3TDo28gZG9zIGNvbnRhY3RvcyAodGVtcG8gdG90YWwgZG8gcHJvY2Vzc28gZSB0b3RhbCBkZSBjaGFtYWRhcyBwZXJkaWRhcykNCg0KIyMgU2l0dWHDp8OjbyBhdHVhbA0KDQoxLiBBcGVuYXMgMiBhZG1pbmlzdHJhdGl2b3Mgbm8gc2VydmnDp28sIGFtYm9zIGNvbWXDp2FtIMOgcyA4aCBlIGFjYWJhbSDDoHMgMThoICgxMGggZGUgdHJhYmFsaG8pIGRlIGNhZGEgZGlhIMO6dGlsLg0KDQoyLiBPIHRlbXBvIGVudHJlIGNoZWdhZGFzIHRlbSBleHAoMS80MCkgKHBxIG1lZGlhIGRlIDQwL2gpDQoNCjMuIFRpcG8gZGUgY29udGFjdG86DQoqIHByb2IgZGUgdGVsZWZvbmUgPSAwLjg1DQoqIHByb2IgZGUgYXBwID0gMC4xNQ0KDQo0LiBUaXBvIGRlIGNvbnN1bHRhOiANCiogMC4zIG7Do28gw6kgYWdlbmRhZG8gY29uc3VsdGEsIA0KKiAwLjYgY29uc3VsYSBwb3IgdGVsZWZvbmUsIA0KKiAwLjEgcHJlc2VuY2lhbA0KDQo1LiBUZW1wbyBkZSANCiogQ29udmVyc2EgYW50ZXMgZGEgZGVjaXNhbyDDqSBub3JtKDQsMV4yKQ0KKiBUb21hZGEgZGUgZGVjaXNhbyBkbyBhZG1pbnN0cmF0aXZvIMOpIG5vcm0oMSwwLjI1XjIpDQoqIFRlbXBvIGRlIGFnZW5kYW1lbnRvIFNFIE5FQ0VTU8OBUklPIMOpIG5vcm0oMSwwLjI1XjIpDQoNCjYuIDVvIHRlbGVmb25lIGRlc2xpZ2EgYXBvcyA1IG1pbnV0b3MgZGUgZXNwZXJhIChlIGZpY2EgY2hhbWFkYSBwZXJkaWRhKSANCg0KDQpgYGB7cn0NCmxpYnJhcnkoc2ltbWVyKQ0KbGlicmFyeShzaW1tZXIuYnJpY2tzKQ0KbGlicmFyeShzaW1tZXIucGxvdCkNCmNvbmZsaWN0c19wcmVmZXIoc2ltbWVyOjpyb2xsYmFjaykNCmNvbmZsaWN0c19wcmVmZXIoc2ltbWVyOjpub3cpDQpjb25mbGljdHNfcHJlZmVyKGRwbHlyOjpzZWxlY3QpDQojIHRoaXMgd2lsbCBiZSBzdWJzdGl0dXRlZCBiZWxvdyBhbmQgbGl0ZXJhbGx5IGp1c3QgaXMgaGVyZSBiZWNhdXNlIHNpbW1lciBkb2Vzbid0IGtub3cgaG93IHRvIGltcGxlbWVudCBwaXBlcyBhbmQgSSBoYXRlIGl0IGhlcmUNCmVudiA8LSBzaW1tZXIoImhvc3BpdGFsIikgDQpgYGANClJlc291cmNlczoNCiAgU2VydmVyOiBPIGhvc3BpdGFsIGNvbSAyIGFkbWluaXN0cmFkb3Jlcw0KICBRdWV1ZTogRmlsYSBkZSBlc3BlcmEgY29tIHByaW9yaWRhZGUgZGUgY2xpZW50cyBlbSBjaGFtYWRhDQogICAgIE7Do28gaMOhIHZhbnRnZW0gZW0gZXNjb2xoZXIgYSBhcHAgcXVhbmRvIGjDoSBjaGFtYWRhIGRlIGVzcGVyYQ0KTWFuYWdlcjogdmFpIGF0cmlidWlyIG8gdGlwbyBkZSBjb25zdWx0YSAoZSBWYWkgYWx0ZXJhciBvIHRpcG8gZGUgY2xpZW50ZSBzZSBlbGUgZm9yIHJlamVpdGFkbyBuYSBjaGFtYWRhPykNCkdlcmFkb3I6IHZhaSBnZXJpciBub3ZvcyBhcnJpdmFscyAoZXhwKDEvNDApKQ0KYXJyaXZhbDogY2FkYSBwYWNpZW50ZSAocXVlIGxldmEgdW1hIHRyYWplY3RvcnkpDQp0cmFqZWN0b3J5OiBhIHJlY2VpdGEgZGUgY2FkYSBhcnJpdmFsDQoNCmBgYHtyfQ0KIyBhdHJpYnV0ZXMgbmVlZCB0byBiZSBudW1lcmljDQp0aXBvX2RlX2NvbnN1bHRhLlBSRVNFTkNJQUwgPC0gMQ0KdGlwb19kZV9jb25zdWx0YS5URUxFRk9OSUNBIDwtIDINCnRpcG9fZGVfY29uc3VsdGEuTkFPX0FHRU5EQURBIDwtIDMNCnRpcG9zX2RlX2NvbnN1bHRhIDwtIGModGlwb19kZV9jb25zdWx0YS5QUkVTRU5DSUFMLCB0aXBvX2RlX2NvbnN1bHRhLlRFTEVGT05JQ0EsIHRpcG9fZGVfY29uc3VsdGEuTkFPX0FHRU5EQURBKQ0KdGlwb3NfZGVfY29uc3VsdGEucHJvYnMgPC0gYygwLjEsIDAuNiwgMC4zKQ0KDQp0aXBvX2RlX2NvbnRhY3RvLlRFTEVGT05FIDwtIDENCnRpcG9fZGVfY29udGFjdG8uQVBQIDwtIDINCnRpcG9zX2RlX2NvbnRhY3RvIDwtIGModGlwb19kZV9jb250YWN0by5URUxFRk9ORSwgdGlwb19kZV9jb250YWN0by5BUFApDQp0aXBvc19kZV9jb250YWN0by5wcm9icyA8LSBjKDAuODUsIDAuMTUpDQoNCnRlbXBvX2F0ZW5kaWRvIDwtIGZ1bmN0aW9uKCkgew0KICBtZWFuX3RpbWVfdHJpYWdlbSA8LSBybm9ybSgxLCBtZWFuID0gNCwgc2QgPSAxKQ0KICBtZWFuX3RpbWVfZGVjaXNpb24gPC0gcm5vcm0oMSwgbWVhbiA9IDEsIHNkID0gMC4yNSkNCiAgdGltZV90b190aW1lb3V0IDwtIG1lYW5fdGltZV90cmlhZ2VtICsgbWVhbl90aW1lX2RlY2lzaW9uDQogIA0KICBpZiAoZ2V0X2F0dHJpYnV0ZShlbnYsICJ0aXBvX2RlX2NvbnN1bHRhIikgIT0gdGlwb19kZV9jb25zdWx0YS5OQU9fQUdFTkRBREEpIHsNCiAgICB0aW1lX3RvX3RpbWVvdXQgPC0gdGltZV90b190aW1lb3V0ICsgcm5vcm0oMSwgbWVhbiA9IDEsIHNkID0gMC4yNSkNCiAgfQ0KICANCiAgcmV0dXJuKHRpbWVfdG9fdGltZW91dCkNCn0NCg0KaW50aW1lIDwtIGZ1bmN0aW9uKCkgew0KICBub3coZW52KSUlIDI0KjYwIC0+IG4NCiAgcmV0dXJuKDgqNjAgPCBuICYgbiA8IDE4KjYwKQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQpwZXNzb2EgPC0gdHJhamVjdG9yeSgicGVzc29hIikgJT4lDQogICMgc2V0X2F0dHJpYnV0ZSgidGltZXNfcGhvbmVfcmVqZWN0ZWQiLCAwKSAlPiUgDQogICMgc2V0X2F0dHJpYnV0ZSgicGhvbmVfcmVqZWN0ZWQiLCAwKSAlPiUNCiAgc2V0X2F0dHJpYnV0ZSgidGlwb19kZV9jb25zdWx0YSIsIFwoKSBzYW1wbGUodGlwb3NfZGVfY29uc3VsdGEsIDEsIHByb2IgPSB0aXBvc19kZV9jb25zdWx0YS5wcm9icykpICU+JQ0KICBzZXRfYXR0cmlidXRlKCJ0aXBvX2RlX2NvbnRhY3RvIiwgXCgpIHNhbXBsZSh0aXBvc19kZV9jb250YWN0bywgMSwgcHJvYiA9IHRpcG9zX2RlX2NvbnRhY3RvLnByb2JzKSwgdGFnID0gImNvbnRhY3RvIikgJT4lDQogIHNldF9wcmlvcml0aXphdGlvbihcKCkgaWYgKGdldF9hdHRyaWJ1dGUoZW52LCAidGlwb19kZV9jb250YWN0byIpID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUpIGMoMSwgLTEsIEZBTFNFKSBlbHNlIGMoMCAsIC0xLCBGQUxTRSkpICU+JSAjIFBSSU9SSVRZLCBEUk9QIFBSSU9SSVRZLCBSRVNUQVJUDQogICMgbG9nXyhcKCkgcGFzdGUoIlZvdSBwcmEgZmlsYSBhZ29yYSwgZXN0b3UgYSB1c2FyIiwgaWZlbHNlKGdldF9hdHRyaWJ1dGUoZW52LCAidGlwb19kZV9jb250YWN0byIpID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUsICJvIHRlbGVmb25lIiwgImEgYXBwIikpKSAlPiUNCiAgcmVuZWdlX2luKFwoKSBpZmVsc2UoZ2V0X2F0dHJpYnV0ZShlbnYsICJ0aXBvX2RlX2NvbnRhY3RvIikgPT0gdGlwb19kZV9jb250YWN0by5URUxFRk9ORSwgNSwgSW5mKSwgb3V0ID0gDQogICAgICAgICAgICAgIHRyYWplY3RvcnkoKSAlPiUgc2V0X2F0dHJpYnV0ZSgicGhvbmVfcmVqZWN0ZWQiLCAxKQ0KICAgICAgICAgICAgICAjIE8gYm90YXMgbsOjbyBnb3N0b3UNCiAgICAgICAgICAgICAgIyB0cmFqZWN0b3J5KCJwZXNzb2FfdHJ5X2FnYWluIikgJT4lIA0KICAgICAgICAgICAgICAjIHNldF9hdHRyaWJ1dGUoInRpbWVzX3Bob25lX3JlamVjdGVkIiwgXCgpIGdldF9hdHRyaWJ1dGUoZW52LCAidGltZXNfcGhvbmVfcmVqZWN0ZWQiKSArIDEpICU+JSANCiAgICAgICAgICAgICAgIyAjIGxlYXZlIGlmIG91dCBvZiBzY2hlZHVsZQ0KICAgICAgICAgICAgICAjIGxlYXZlKFwoKSB7aWZlbHNlKGludGltZSgpLCAwLCAxKX0pICU+JSANCiAgICAgICAgICAgICAgIyAjIGxvZ18oXCgpICJOYW8gZnVpIGF0ZW5kaWRvLCB2b3Ugdm9sdGFyIHByYSBmaWxhIikgJT4lIA0KICAgICAgICAgICAgICAjIHJvbGxiYWNrKHRhcmdldD0iY29udGFjdG8iLCB0aW1lcyA9IDEpICMgbsOjbyBmdW5jaW9uYSwgbXVkYXIgcHJhIG51bWVybz8NCiAgICAgICAgICAgICkgJT4lDQogIHNlaXplKCJhZG1pbmlzdHJhZG9yIiwgMSkgJT4lDQogIHJlbmVnZV9hYm9ydCgpICU+JQ0KICAjIGxvZ18oXCgpIHBhc3RlKCJCZWZvcmUgdGltZW91dCIpKSAlPiUgDQogIHNldF9hdHRyaWJ1dGUoImF0ZW5kaWRvIiwgMSkgJT4lIA0KICB0aW1lb3V0KHRlbXBvX2F0ZW5kaWRvKSAlPiUgDQogICMgbG9nXyhcKCkgIkFmdGVyIHRpbWVvdXQiKSAlPiUgDQogIHJlbGVhc2UoImFkbWluaXN0cmFkb3IiLCAxKQ0KICAjIGxvZ18oXCgpICJMZWF2aW5nIikNCg0KcGVzc29hDQpgYGANCg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQojIDUwIHJlcGxpY2FzDQplbnZzIDwtIGxhcHBseSgxOjUwLCBmdW5jdGlvbihpKSB7DQogIGVudiA8PC0gc2ltbWVyKCJob3NwaXRhbCIpIA0KICBlbnYgJT4lIA0KICAgIGFkZF9nZW5lcmF0b3IoInBlc3NvYSIsIHBlc3NvYSwgZnJvbV90byg4KjYwLCAxOCo2MCwgXCgpIHJleHAoMSwgNDAvNjApLCBldmVyeSA9IDI0KjYwLCBhcnJpdmUgPSBGKSwgbW9uID0gMikgJT4lIA0KICAgIGFkZF9yZXNvdXJjZSgiYWRtaW5pc3RyYWRvciIsIHNjaGVkdWxlKGMoOCo2MCwgMTgqNjApLCBjKDIsIDApLCBwZXJpb2QgPSAyNCo2MCkpICU+JQ0KICAgIHJ1bigyNCo2MCo1KQ0KfSkNCg0KZW52cyAlPiUgc2ltbWVyLnBsb3Q6OmdldF9tb25fYXJyaXZhbHMob25nb2luZyA9IFQpIC0+IGFycml2YWxzIA0KZW52cyAlPiUgc2ltbWVyLnBsb3Q6OmdldF9tb25fYXR0cmlidXRlcygpIC0+IGF0dHJpYnV0ZXMNCmVudnMgJT4lIHNpbW1lci5wbG90OjpnZXRfbW9uX3Jlc291cmNlcygpIC0+IHJlc291cmNlcw0KYGBgDQoNCg0KYGBge3J9DQoNCmFycml2YWxzICU+JQ0KICBmaWx0ZXIoc3RhcnRfdGltZSAhPSAtMSkgJT4lICMgbiBzZWkgcHEgZSBxIGlzdG8gYWNvbnRlY2UNCiAgYXJyYW5nZShzdGFydF90aW1lKSAlPiUgDQogIHNlbGVjdCgtYWN0aXZpdHlfdGltZSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGF5ID0gY2VpbGluZyhzdGFydF90aW1lLygyNCo2MCkpLA0KICAgIHN0YXJ0X3RpbWVfZGF5ID0gc3RhcnRfdGltZSAlJSAoMjQqNjApICU+JSBzZWNvbmRzX3RvX3BlcmlvZCgpLA0KICAgIGVuZF90aW1lX2RheSA9IGVuZF90aW1lICUlICgyNCo2MCkgJT4lIHNlY29uZHNfdG9fcGVyaW9kKCksDQogICAgIyByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSwNCiAgKSAtPiBhcnJpdmFscy5kZjENCmFycml2YWxzLmRmMSAlPiUgZmlsdGVyKHJlcGxpY2F0aW9uID09IDEpICU+JSBtdXRhdGUoc3RhcnRfdGltZV9kYXkgPSBzdGFydF90aW1lX2RheSAlPiUgcm91bmQoMyksIGVuZF90aW1lX2RheSA9IGVuZF90aW1lX2RheSAlPiUgcm91bmQoMykpDQoNCg0KDQojIHR1cm4gd2hhdCdzIGFib3ZlIGludG8gYSBmdW5jdGlvbiBzbyBpdCBjYW4gYmUgZG9uZSBmb3IgMmEgYW5kIDJiDQojIGdldF9hcnJpdmFscyA8LSBmdW5jdGlvbihtb25fYXR0cmlidXRlcykgew0KIyAgIG1vbl9hdHRyaWJ1dGVzICU+JQ0KIyAgICAgZmlsdGVyKHN0YXJ0X3RpbWUgIT0gLTEpICU+JSAjIG4gc2VpIHBxIGUgcSBpc3RvIGFjb250ZWNlDQojICAgICBhcnJhbmdlKHN0YXJ0X3RpbWUpICU+JSANCiMgICAgIHNlbGVjdCgtYWN0aXZpdHlfdGltZSkgJT4lIA0KIyAgICAgbXV0YXRlKA0KIyAgICAgICBkYXkgPSBjZWlsaW5nKHN0YXJ0X3RpbWUvKDI0KjYwKSksDQojICAgICAgIHN0YXJ0X3RpbWVfZGF5ID0gc3RhcnRfdGltZSAlJSAoMjQqNjApICU+JSBzZWNvbmRzX3RvX3BlcmlvZCgpLA0KIyAgICAgICBlbmRfdGltZV9kYXkgPSBlbmRfdGltZSAlJSAoMjQqNjApICU+JSBzZWNvbmRzX3RvX3BlcmlvZCgpLA0KIyAgICAgICAjIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpLA0KIyAgICAgKQ0KIyB9DQojIA0KIyBwcHJpbnRfYXJyaXZhbHMgPC0gZnVuY3Rpb24gKGFycml2YWxzLmRmMSkgew0KIyAgIGFycml2YWxzLmRmMSAlPiUgZmlsdGVyKHJlcGxpY2F0aW9uID09IDEpICU+JSBtdXRhdGUoc3RhcnRfdGltZV9kYXkgPSBzdGFydF90aW1lX2RheSAlPiUgcm91bmQoMyksIGVuZF90aW1lX2RheSA9ICMgZW5kX3RpbWVfZGF5ICU+JSByb3VuZCgzKSkNCiMgfQ0KDQpgYGANCg0KYGBge3J9DQojIGFuYWxpc2UgZGUgZWZpY2llbmNpYQ0KIyBudW1lcm8gZGUgY2hhbWFkYXMgcGVyZGlkYXMNCiMgYWRkIGRheSB0byBhdHJpYnV0ZXMgZnJvbSBhcnJpdmFscy5kZjENCmF0dHJpYnV0ZXMgJT4lIA0KICBsZWZ0X2pvaW4oYXJyaXZhbHMuZGYxICU+JSBzZWxlY3QobmFtZSwgcmVwbGljYXRpb24sIGRheSwgc3RhcnRfdGltZSwgZW5kX3RpbWUsIGVuZF90aW1lX2RheSksIGJ5ID0gYygibmFtZSIsICJyZXBsaWNhdGlvbiIpKSAlPiUgDQogIHJlbmFtZShhdHJpYnV0ZV90aW1lID0gdGltZSwgcGVzc29hX3N0YXJ0X3RpbWUgPSBzdGFydF90aW1lLCBwZXNzb2FfZW5kX3RpbWUgPSBlbmRfdGltZSkgJT4lIA0KICBzZWxlY3QoYXRyaWJ1dGVfdGltZSwgbmFtZSwga2V5LCB2YWx1ZSwgcmVwbGljYXRpb24sIGRheSwgcGVzc29hX3N0YXJ0X3RpbWUsIHBlc3NvYV9lbmRfdGltZSkgLT4gYXR0cmlidXRlcy5kZjENCmF0dHJpYnV0ZXMuZGYxIA0KYGBgDQoNCmBgYHtyfQ0KIyBwZXNzb2FzIGUgb3Mgc2V1cyB0aXBvcyBkZSBjaGFtYWRhDQphdHRyaWJ1dGVzLmRmMSAlPiUgDQogIGZpbHRlcihrZXkgPT0gInRpcG9fZGVfY29udGFjdG8iKSAlPiUgDQogIHNlbGVjdChuYW1lLCByZXBsaWNhdGlvbiwgdmFsdWUpICU+JSANCiAgbXV0YXRlKHZhbHVlID0gaWZlbHNlKHZhbHVlID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUsICJ0ZWxlZm9uZSIsICJhcHAiKSkgJT4lDQogIHJlbmFtZShjb250YWN0byA9IHZhbHVlKSAtPiBjb250YWN0b3MuZGYxDQpgYGANCg0KDQpgYGB7cn0NCiMgcGVyY2VudGFnZW0gZGUgY2hhbWFkYXMgcGVyZGlkYXMNCmF0dHJpYnV0ZXMuZGYxICU+JSANCiAgZmlsdGVyKGtleSA9PSAidGlwb19kZV9jb250YWN0byIsIHZhbHVlID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUpICU+JSANCiAgbGVmdF9qb2luKGF0dHJpYnV0ZXMuZGYxICU+JSBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSBtdXRhdGUoZ2F2ZV91cCA9IGtleSkgJT4lIHNlbGVjdChuYW1lLCByZXBsaWNhdGlvbiwgZ2F2ZV91cCksIGpvaW5fYnkoIm5hbWUiLCAicmVwbGljYXRpb24iKSkgJT4lIA0KICBtdXRhdGUoZ2F2ZV91cCA9IGlmX2Vsc2UoZ2F2ZV91cCAlPiUgaXMubmEsIFQsIEYpLCByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IpICU+JSANCiAgc2VsZWN0KG5hbWUsIHJlcGxpY2F0aW9uLCBwZXNzb2FfZW5kX3RpbWUsIGdhdmVfdXApIC0+IHBob25lX2NhbGxzDQpwaG9uZV9jYWxscw0KYGBgDQoNCmBgYHtyfQ0KIyBwaG9uZV9jYWxscyByYXRlIHBlciByZXBsaWNhdGlvbg0KcGhvbmVfY2FsbHMgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICBzdW1tYXJpc2UocGhvbmVfY2FsbHMgPSBuKCksIGdhdmVfdXAgPSBzdW0oZ2F2ZV91cCkpICU+JSANCiAgbXV0YXRlKHBob25lX2NhbGxzX3Blcl9ob3VyID0gcGhvbmVfY2FsbHMvMTAsIGdhdmVfdXBfcmF0ZSA9IGdhdmVfdXAvcGhvbmVfY2FsbHMpICU+JSANCiAgYXJyYW5nZShkZXNjKGdhdmVfdXBfcmF0ZSkpIC0+IHBob25lX2NhbGxzX3JhdGUNCnBob25lX2NhbGxzX3JhdGUNCg0KcGhvbmVfY2FsbHNfcmF0ZSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIocmVwbGljYXRpb24sIGdhdmVfdXBfcmF0ZSksIHkgPSBnYXZlX3VwX3JhdGUpKSArDQogIGdlb21fY29sKCkgKyANCiAgeWxpbSgwLCAxKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNzAsIGhqdXN0ID0gMSkpICsNCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1pbihnYXZlX3VwX3JhdGUpKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKw0KICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWF4KGdhdmVfdXBfcmF0ZSkpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibHVlIikgKw0KICB4bGFiKCJSZXBsaWNhdGlvbiIpICsgDQogIHlsYWIoIkdhdmUgdXAgcmF0ZSIpDQpgYGANCg0KYGBge3J9DQpwaG9uZV9jYWxsc19yYXRlICU+JSANCiAgc3VtbWFyaXNlKA0KICAgIG1pbiA9IG1pbihwaG9uZV9jYWxsc19wZXJfaG91ciksIA0KICAgIG1lYW4gPSBtZWFuKHBob25lX2NhbGxzX3Blcl9ob3VyKSwgDQogICAgbWF4ID0gbWF4KHBob25lX2NhbGxzX3Blcl9ob3VyKQ0KICAgICkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBldm9sdWNhbyBhbyBsb25nbyBkbyBkaWEgRE8gUVVFPw0KYXR0cmlidXRlcy5kZjEgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSANCiAgbXV0YXRlKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICByZWZyYW1lKHRpbWVfcmVqZWN0ZWQgPSBwZXNzb2FfZW5kX3RpbWUvNjAsIHN1bWN1bV9wZXJfZGF5ID0gY3Vtc3VtKHZhbHVlKSwgZGF5ID0gZGF5KSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWVfcmVqZWN0ZWQsIHkgPSBzdW1jdW1fcGVyX2RheSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KDQpgYGB7cn0NCiMgZ2V0IHF1ZXVlIHNpemUgaGlzdG9ncmFtDQpyZXNvdXJjZXMgJT4lIA0KICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZS82MCwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSkpICsNCiAgZ2VvbV9zdGVwKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCnJlc291cmNlcyAlPiUgDQogIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLzYwLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSkgKw0KICBnZW9tX3N0ZXAoYWVzKGNvbG9yID0gcmVwbGljYXRpb24pLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICB5bGltKDAsMjUwKQ0KICANCg0KYGBgDQoNCg0KYGBge3J9DQojIGpvaW4gdGhlc2UgMiBwbG90cw0KYXR0cmlidXRlcy5kZjEgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSANCiAgbXV0YXRlKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICByZWZyYW1lKHRpbWVfcmVqZWN0ZWQgPSBwZXNzb2FfZW5kX3RpbWUvNjAsIHN1bWN1bV9wZXJfZGF5ID0gY3Vtc3VtKHZhbHVlKSwgZGF5ID0gZGF5KSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWVfcmVqZWN0ZWQsIHkgPSBzdW1jdW1fcGVyX2RheSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgZ2VvbV9zdGVwKGRhdGEgPSByZXNvdXJjZXMgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZS82MCwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSksIA0KICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIGFscGhhID0gMC4zKSArIA0KICBnZW9tX3Ntb290aChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICAgICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUvNjAsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICAgIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSksIA0KICAgICAgICAgICAgICBhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSwgbWV0aG9kPSJsbSIpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICB5bGltKDAsODc1KQ0KICANCmF0dHJpYnV0ZXMuZGYxICU+JSANCiAgZmlsdGVyKGtleSA9PSAicGhvbmVfcmVqZWN0ZWQiKSAlPiUgDQogIG11dGF0ZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JSANCiAgcmVmcmFtZSh0aW1lX3JlamVjdGVkID0gcGVzc29hX2VuZF90aW1lLzYwLCBzdW1jdW1fcGVyX2RheSA9IGN1bXN1bSh2YWx1ZSksIGRheSA9IGRheSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3JlamVjdGVkLCB5ID0gc3VtY3VtX3Blcl9kYXkpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArDQogIGdlb21fc21vb3RoKCkgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGdlb21fc3RlcChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUvNjAsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLzYwLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIG1ldGhvZD0ibG0iKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCg0KYXR0cmlidXRlcy5kZjEgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSANCiAgbXV0YXRlKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICByZWZyYW1lKHRpbWVfcmVqZWN0ZWQgPSBwZXNzb2FfZW5kX3RpbWUvNjAsIHN1bWN1bV9wZXJfZGF5ID0gY3Vtc3VtKHZhbHVlKSwgZGF5ID0gZGF5KSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWVfcmVqZWN0ZWQsIHkgPSBzdW1jdW1fcGVyX2RheSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQoNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGdlb21fc3RlcChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUvNjAsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLzYwLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIG1ldGhvZD0ibG0iKSArDQoNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCmBgYHtyfQ0KIyB0ZW1wbyBkZSBlc3BlcmEgZGFxdWVsZXMgcXVlIGNvbmNsdWlyYW0sIHNwZWFyZGFvIHBvciBjaGFtYWRhIGUgbWVuc2FnZW0NCmF0dHJpYnV0ZXMgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJhdGVuZGlkbyIpICU+JSANCiAgbGVmdF9qb2luKGFycml2YWxzLmRmMSAlPiUgc2VsZWN0KHN0YXJ0X3RpbWUsIG5hbWUsIHJlcGxpY2F0aW9uKSwgam9pbl9ieSgibmFtZSIsICJyZXBsaWNhdGlvbiIpKSAlPiUgDQogIGxlZnRfam9pbihjb250YWN0b3MuZGYxLCBqb2luX2J5KCJuYW1lIiwgInJlcGxpY2F0aW9uIikpICU+JSANCiAgbXV0YXRlKHRpbWVfd2FpdGluZyA9IHRpbWUgLSBzdGFydF90aW1lKSAlPiUgDQogIHNlbGVjdChuYW1lLCB2YWx1ZSwgcmVwbGljYXRpb24sIHRpbWVfd2FpdGluZywgY29udGFjdG8pIC0+IHdhaXRpbmdfdGltZXMNCndhaXRpbmdfdGltZXMNCmBgYA0KDQoNCmBgYHtyfQ0KIyBoaXN0b2dyYW1hIChuIHRvdSBhIHBlcmNlYmVyIHBxIMOpIHEgaMOhIHdhaXRpbmcgdGltZXMgbWFpb3JlcyBxdWUgNSkNCndhaXRpbmdfdGltZXMgJT4lIA0KICBmaWx0ZXIoY29udGFjdG8gPT0gInRlbGVmb25lIikgJT4lDQogIGdncGxvdChhZXMoeCA9IHRpbWVfd2FpdGluZykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwMCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCndhaXRpbmdfdGltZXMgJT4lIA0KICBmaWx0ZXIoY29udGFjdG8gPT0gImFwcCIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3dhaXRpbmcvNjApKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDApICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCg0KYGBge3J9DQojIHJlZ3VsYXIgd2FpdGluZyB0aW1lIHN0YXRzIHdpdGhvdXQgZ3JhcGhzDQp3YWl0aW5nX3RpbWVzICU+JSANCiAgZ3JvdXBfYnkoY29udGFjdG8pICU+JSANCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKHRpbWVfd2FpdGluZyksIHNkID0gc2QodGltZV93YWl0aW5nKSwgbWVkaWFuID0gbWVkaWFuKHRpbWVfd2FpdGluZykpIA0Kd2FpdGluZ190aW1lcyAlPiUgDQogIGdyb3VwX2J5KGNvbnRhY3RvKSAlPiUgDQogIHN1bW1hcmlzZShxdWFudGlsZV8xMCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC4xKSwgcXVhbnRpbGVfMjAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuMiksIHF1YW50aWxlXzMwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjMpLCBxdWFudGlsZV80MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC40KSwgcXVhbnRpbGVfNTAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuNSksIHF1YW50aWxlXzYwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjYpLCBxdWFudGlsZV83MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC43KSwgcXVhbnRpbGVfODAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuOCksIHF1YW50aWxlXzkwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjkpLCBxdWFudGlsZV85NSA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC45NSksIHF1YW50aWxlXzk5ID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjk5KSkNCmBgYA0KDQoNCmBgYHtyfQ0KcmVzb3VyY2VzICU+JSBwbG90KG1ldHJpYyA9ICJ1dGlsaXphdGlvbiIpDQpyZXNvdXJjZXMgJT4lIHBsb3QobWV0cmljID0gInVzYWdlIiwgc3RlcHMgPSBUKQ0KDQpgYGANCiMjIFBlcmd1bnRhIDIgDQoNCkFsw61uZWFzIGNvbSBtdWRhbsOnYXMgcmVsYXRpdmFzIMOgcyBpbnRlcnJ1cMOnw7Vlcw0KDQpgYGB7cn0NCiMgbXVkYW7Dp2FzDQplbnZzIDwtIGxhcHBseSgxOjUwLCBmdW5jdGlvbihpKSB7DQogIGVudiA8PC0gc2ltbWVyKCJob3NwaXRhbCIpIA0KICBlbnYgJT4lIA0KICAgIGFkZF9nZW5lcmF0b3IoInBlc3NvYV9kZV9tYW5oYSIsIHBlc3NvYSwgZnJvbV90byg4KjYwLCAxMCo2MCwgXCgpIHJleHAoMSwgKDEyMC8yKS82MCksIGV2ZXJ5ID0gMjQqNjAsIGFycml2ZSA9IEYpLCBtb24gPSAyKSAlPiUgDQogICAgYWRkX2dlbmVyYXRvcigicGVzc29hX2RlX3RhcmRlIiwgcGVzc29hLCBmcm9tX3RvKDEwKjYwLCAxNio2MCwgXCgpIHJleHAoMSwgKDI0MC82KS82MCksIGV2ZXJ5ID0gMjQqNjAsIGFycml2ZSA9IEYpLCBtb24gPSAyKSAlPiUgDQogICAgYWRkX2dlbmVyYXRvcigicGVzc29hX2RlX25vaXRlIiwgcGVzc29hLCBmcm9tX3RvKDE2KjYwLCAxOCo2MCwgXCgpIHJleHAoMSwgKDQwLzIpLzYwKSwgZXZlcnkgPSAyNCo2MCwgYXJyaXZlID0gRiksIG1vbiA9IDIpICU+JSANCiAgICBhZGRfcmVzb3VyY2UoImFkbWluaXN0cmFkb3IiLCBzY2hlZHVsZSgNCiAgICAgIGMoOCAsIDkgLCA5LjUgLCAxMiAsIDE1LjUgLDE4KSo2MCwgDQogICAgICBjKCAgMSAsIDIsICAgIDQsICAgMywgICAgIDEsICAwKSwgDQogICAgICBwZXJpb2QgPSAyNCo2MA0KICAgICkpICU+JQ0KICAgIHJ1bigyNCo2MCo1KQ0KfSkNCg0KZW52cyAlPiUgc2ltbWVyLnBsb3Q6OmdldF9tb25fYXJyaXZhbHMob25nb2luZyA9IFQpIC0+IGFycml2YWxzDQplbnZzICU+JSBzaW1tZXIucGxvdDo6Z2V0X21vbl9hdHRyaWJ1dGVzKCkgLT4gYXR0cmlidXRlcw0KZW52cyAlPiUgc2ltbWVyLnBsb3Q6OmdldF9tb25fcmVzb3VyY2VzKCkgLT4gcmVzb3VyY2VzDQpgYGANCg0KIyMgMi5iKQ0KDQpgYGB7cn0NCiMgYikNCmFycml2YWxzICU+JQ0KICBmaWx0ZXIoc3RhcnRfdGltZSAhPSAtMSkgJT4lICMgbiBzZWkgcHEgZSBxIGlzdG8gYWNvbnRlY2UNCiAgYXJyYW5nZShzdGFydF90aW1lKSAlPiUgDQogIHNlbGVjdCgtYWN0aXZpdHlfdGltZSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGF5ID0gY2VpbGluZyhzdGFydF90aW1lLygyNCo2MCkpLA0KICAgIHN0YXJ0X3RpbWVfZGF5ID0gc3RhcnRfdGltZSAlJSAoMjQqNjApICU+JSBzZWNvbmRzX3RvX3BlcmlvZCgpLA0KICAgIGVuZF90aW1lX2RheSA9IGVuZF90aW1lICUlICgyNCo2MCkgJT4lIHNlY29uZHNfdG9fcGVyaW9kKCksDQogICAgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCksDQogICkgLT4gYXJyaXZhbHMuZGYyYg0KYXJyaXZhbHMuZGYyYiAlPiUgZmlsdGVyKHJlcGxpY2F0aW9uID09IDEpICU+JSBtdXRhdGUoc3RhcnRfdGltZV9kYXkgPSBzdGFydF90aW1lX2RheSAlPiUgcm91bmQoMyksIGVuZF90aW1lX2RheSA9IGVuZF90aW1lX2RheSAlPiUgcm91bmQoMykpDQpgYGANCg0KYGBge3J9DQphcnJpdmFscy5kZjJiJHJlcGxpY2F0aW9uIDwtIGFzLmludGVnZXIoYXMuY2hhcmFjdGVyKGFycml2YWxzLmRmMmIkcmVwbGljYXRpb24pKQ0KDQojIHRlbXBvIGRlIGVzcGVyYSBkYXF1ZWxlcyBxdWUgY29uY2x1aXJhbSwgc3BlYXJkYW8gcG9yIGNoYW1hZGEgZSBtZW5zYWdlbQ0KYXJyaXZhbHMuZGYyYiAlPiUgDQogIGZpbHRlcihmaW5pc2hlZCA9PSBUUlVFKSAlPiUgDQogIG11dGF0ZSh3YWl0aW5nX3RpbWUgPSBlbmRfdGltZSAtIHN0YXJ0X3RpbWUpICU+JSANCiAgbGVmdF9qb2luKGF0dHJpYnV0ZXMgJT4lIGZpbHRlcihrZXkgPT0gInRpcG9fZGVfY29udGFjdG8iKSwgYnkgPSBjKCJuYW1lIiwgInJlcGxpY2F0aW9uIikpICU+JSANCiAgbXV0YXRlKHRpcG9fZGVfY29udGFjdG8gPSBpZmVsc2UodmFsdWUgPT0gdGlwb19kZV9jb250YWN0by5URUxFRk9ORSwgInRlbGVmb25lIiwgIm1lbnNhZ2VtIikgJT4lIGFzX2ZhY3RvciwgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKSAlPiUNCiAgc2VsZWN0KG5hbWUsIHJlcGxpY2F0aW9uLCB3YWl0aW5nX3RpbWUsIHRpcG9fZGVfY29udGFjdG8pIC0+IHdhaXRpbmdfdGltZXMuMmINCndhaXRpbmdfdGltZXMuMmINCmBgYA0KDQpgYGB7cn0NCiMgYW5hbGlzZSBkZSBlZmljaWVuY2lhDQojIG51bWVybyBkZSBjaGFtYWRhcyBwZXJkaWRhcw0KIyBhZGQgZGF5IHRvIGF0cmlidXRlcyBmcm9tIGFycml2YWxzLmRmMmINCmF0dHJpYnV0ZXMgJT4lIA0KICBsZWZ0X2pvaW4oYXJyaXZhbHMuZGYyYiAlPiUgc2VsZWN0KG5hbWUsIHJlcGxpY2F0aW9uLCBkYXksIHN0YXJ0X3RpbWUsIGVuZF90aW1lLCBlbmRfdGltZV9kYXkpLCBieSA9IGMoIm5hbWUiLCAicmVwbGljYXRpb24iKSkgJT4lIA0KICByZW5hbWUoYXRyaWJ1dGVfdGltZSA9IHRpbWUsIHBlc3NvYV9zdGFydF90aW1lID0gc3RhcnRfdGltZSwgcGVzc29hX2VuZF90aW1lID0gZW5kX3RpbWUpICU+JSANCiAgc2VsZWN0KGF0cmlidXRlX3RpbWUsIG5hbWUsIGtleSwgdmFsdWUsIHJlcGxpY2F0aW9uLCBkYXksIHBlc3NvYV9zdGFydF90aW1lLCBwZXNzb2FfZW5kX3RpbWUpIC0+IGF0dHJpYnV0ZXMuZGYyYg0KYXR0cmlidXRlcy5kZjJiIA0KYGBgDQoNCmBgYHtyfQ0KIyBwZXNzb2FzIGUgb3Mgc2V1cyB0aXBvcyBkZSBjaGFtYWRhDQphdHRyaWJ1dGVzLmRmMmIgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJ0aXBvX2RlX2NvbnRhY3RvIikgJT4lIA0KICBzZWxlY3QobmFtZSwgcmVwbGljYXRpb24sIHZhbHVlKSAlPiUgDQogIG11dGF0ZSh2YWx1ZSA9IGlmZWxzZSh2YWx1ZSA9PSB0aXBvX2RlX2NvbnRhY3RvLlRFTEVGT05FLCAidGVsZWZvbmUiLCAiYXBwIikpICU+JQ0KICByZW5hbWUoY29udGFjdG8gPSB2YWx1ZSkgLT4gY29udGFjdG9zLmRmMmINCmBgYA0KDQoNCmBgYHtyfQ0KIyBwZXJjZW50YWdlbSBkZSBjaGFtYWRhcyBwZXJkaWRhcw0KYXR0cmlidXRlcy5kZjJiICU+JSANCiAgZmlsdGVyKGtleSA9PSAidGlwb19kZV9jb250YWN0byIsIHZhbHVlID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUpICU+JSANCiAgbGVmdF9qb2luKGF0dHJpYnV0ZXMuZGYyYiAlPiUgZmlsdGVyKGtleSA9PSAicGhvbmVfcmVqZWN0ZWQiKSAlPiUgbXV0YXRlKGdhdmVfdXAgPSBrZXkpICU+JSBzZWxlY3QobmFtZSwgcmVwbGljYXRpb24sIGdhdmVfdXApLCBqb2luX2J5KCJuYW1lIiwgInJlcGxpY2F0aW9uIikpICU+JSANCiAgbXV0YXRlKGdhdmVfdXAgPSBpZl9lbHNlKGdhdmVfdXAgJT4lIGlzLm5hLCBULCBGKSwgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKSAlPiUgDQogIHNlbGVjdChuYW1lLCByZXBsaWNhdGlvbiwgcGVzc29hX2VuZF90aW1lLCBnYXZlX3VwKSAtPiBwaG9uZV9jYWxscw0KcGhvbmVfY2FsbHMNCmBgYA0KDQpgYGB7cn0NCiMgcGhvbmVfY2FsbHMgcmF0ZSBwZXIgcmVwbGljYXRpb24NCnBob25lX2NhbGxzICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JSANCiAgc3VtbWFyaXNlKHBob25lX2NhbGxzID0gbigpLCBnYXZlX3VwID0gc3VtKGdhdmVfdXApKSAlPiUgDQogIG11dGF0ZShwaG9uZV9jYWxsc19wZXJfaG91ciA9IHBob25lX2NhbGxzLzEwLCBnYXZlX3VwX3JhdGUgPSBnYXZlX3VwL3Bob25lX2NhbGxzKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhnYXZlX3VwX3JhdGUpKSAtPiBwaG9uZV9jYWxsc19yYXRlDQpwaG9uZV9jYWxsc19yYXRlDQoNCnBob25lX2NhbGxzX3JhdGUgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKHJlcGxpY2F0aW9uLCBnYXZlX3VwX3JhdGUpLCB5ID0gZ2F2ZV91cF9yYXRlKSkgKw0KICBnZW9tX2NvbCgpICsgDQogIHlsaW0oMCwgMSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDcwLCBoanVzdCA9IDEpKSArDQogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtaW4oZ2F2ZV91cF9yYXRlKSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsNCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1heChnYXZlX3VwX3JhdGUpKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmx1ZSIpICsNCiAgeGxhYigiUmVwbGljYXRpb24iKSArIA0KICB5bGFiKCJHYXZlIHVwIHJhdGUiKQ0KDQpgYGANCg0KYGBge3J9DQpwaG9uZV9jYWxsc19yYXRlICU+JSANCiAgc3VtbWFyaXNlKA0KICAgIG1pbiA9IG1pbihwaG9uZV9jYWxsc19wZXJfaG91ciksIA0KICAgIG1lYW4gPSBtZWFuKHBob25lX2NhbGxzX3Blcl9ob3VyKSwgDQogICAgbWF4ID0gbWF4KHBob25lX2NhbGxzX3Blcl9ob3VyKQ0KICAgICkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBldm9sdWNhbyBhbyBsb25nbyBkbyBkaWEgRE8gUVVFPw0KYXR0cmlidXRlcy5kZjJiICU+JSANCiAgZmlsdGVyKGtleSA9PSAicGhvbmVfcmVqZWN0ZWQiKSAlPiUgDQogIG11dGF0ZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JSANCiAgcmVmcmFtZSh0aW1lX3JlamVjdGVkID0gcGVzc29hX2VuZF90aW1lLzYwLCBzdW1jdW1fcGVyX2RheSA9IGN1bXN1bSh2YWx1ZSksIGRheSA9IGRheSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3JlamVjdGVkLCB5ID0gc3VtY3VtX3Blcl9kYXkpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArDQogIGdlb21fc21vb3RoKCkgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCg0KYGBge3J9DQojIGdldCBxdWV1ZSBzaXplIGhpc3RvZ3JhbQ0KcmVzb3VyY2VzICU+JSANCiAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUvNjAsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpKSArDQogIGdlb21fc3RlcChhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArIA0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQpyZXNvdXJjZXMgJT4lIA0KICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZS82MCwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSkpICsNCiAgZ2VvbV9zdGVwKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgeWxpbSgwLDI1MCkNCiAgDQpgYGANCg0KDQpgYGB7cn0NCiMgam9pbiB0aGVzZSAyIHBsb3RzDQphdHRyaWJ1dGVzLmRmMmIgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSANCiAgbXV0YXRlKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICByZWZyYW1lKHRpbWVfcmVqZWN0ZWQgPSBwZXNzb2FfZW5kX3RpbWUvNjAsIHN1bWN1bV9wZXJfZGF5ID0gY3Vtc3VtKHZhbHVlKSwgZGF5ID0gZGF5KSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWVfcmVqZWN0ZWQsIHkgPSBzdW1jdW1fcGVyX2RheSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgZ2VvbV9zdGVwKGRhdGEgPSByZXNvdXJjZXMgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZS82MCwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSksIA0KICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIGFscGhhID0gMC4zKSArIA0KICBnZW9tX3Ntb290aChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICAgICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUvNjAsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICAgIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSksIA0KICAgICAgICAgICAgICBhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSwgbWV0aG9kPSJsbSIpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICB5bGltKDAsODc1KQ0KICANCmF0dHJpYnV0ZXMuZGYyYiAlPiUgDQogIGZpbHRlcihrZXkgPT0gInBob25lX3JlamVjdGVkIikgJT4lIA0KICBtdXRhdGUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKSAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUgDQogIHJlZnJhbWUodGltZV9yZWplY3RlZCA9IHBlc3NvYV9lbmRfdGltZS82MCwgc3VtY3VtX3Blcl9kYXkgPSBjdW1zdW0odmFsdWUpLCBkYXkgPSBkYXkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdGltZV9yZWplY3RlZCwgeSA9IHN1bWN1bV9wZXJfZGF5KSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gcmVwbGljYXRpb24pLCBhbHBoYSA9IDAuMykgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBnZW9tX3N0ZXAoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgICAgICAgICAgICAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLzYwLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSwgDQogICAgICAgICAgICBhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSwgYWxwaGEgPSAwLjMpICsgDQogIGdlb21fc21vb3RoKGRhdGEgPSByZXNvdXJjZXMgJT4lIA0KICAgICAgICAgICAgICAgIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZS82MCwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSwgDQogICAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBtZXRob2Q9ImxtIikgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGdndGl0bGUoIlRhbWFuaG8gZGEgZmlsYSBlIGNoYW1hZGFzIHJlamVpdGFkYXMgMmIpIHBvciBkaWEiKSArDQogIHhsYWIoIlRlbXBvIGRlIHNpbXVsYcOnw6NvIChob3JhcykiKSArIA0KICB5bGFiKCJOw7ptZXJvIGRlIGNoYW1hZGFzIHJlamVpdGFkYXMiKQ0KDQoNCg0KYXR0cmlidXRlcy5kZjJiICU+JSANCiAgZmlsdGVyKGtleSA9PSAicGhvbmVfcmVqZWN0ZWQiKSAlPiUgDQogIG11dGF0ZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JSANCiAgcmVmcmFtZSh0aW1lX3JlamVjdGVkID0gcGVzc29hX2VuZF90aW1lLzYwLCBzdW1jdW1fcGVyX2RheSA9IGN1bXN1bSh2YWx1ZSksIGRheSA9IGRheSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3JlamVjdGVkLCB5ID0gc3VtY3VtX3Blcl9kYXkpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArDQogIGdlb21fc21vb3RoKCkgKw0KDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBnZW9tX3N0ZXAoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgICAgICAgICAgICAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLzYwLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSwgDQogICAgICAgICAgICBhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSwgYWxwaGEgPSAwLjMpICsgDQogIGdlb21fc21vb3RoKGRhdGEgPSByZXNvdXJjZXMgJT4lIA0KICAgICAgICAgICAgICAgIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZS82MCwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSwgDQogICAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBtZXRob2Q9ImxtIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgZ2d0aXRsZSgiVGFtYW5obyBkYSBmaWxhIGUgY2hhbWFkYXMgcmVqZWl0YWRhcyAyYikgbm9zIDUgZGlhcyIpICsNCiAgeGxhYigiVGVtcG8gZGUgc2ltdWxhw6fDo28gKGhvcmFzKSIpICsgDQogIHlsYWIoIk7Dum1lcm8gZGUgY2hhbWFkYXMgcmVqZWl0YWRhcyIpDQoNCmBgYA0KDQpgYGB7cn0NCiMgdGVtcG8gZGUgZXNwZXJhIGRhcXVlbGVzIHF1ZSBjb25jbHVpcmFtLCBzcGVhcmRhbyBwb3IgY2hhbWFkYSBlIG1lbnNhZ2VtDQphdHRyaWJ1dGVzICU+JSANCiAgZmlsdGVyKGtleSA9PSAiYXRlbmRpZG8iKSAlPiUgDQogIGxlZnRfam9pbihhcnJpdmFscy5kZjJiICU+JSBzZWxlY3Qoc3RhcnRfdGltZSwgbmFtZSwgcmVwbGljYXRpb24pLCBqb2luX2J5KCJuYW1lIiwgInJlcGxpY2F0aW9uIikpICU+JSANCiAgbGVmdF9qb2luKGNvbnRhY3Rvcy5kZjJiLCBqb2luX2J5KCJuYW1lIiwgInJlcGxpY2F0aW9uIikpICU+JSANCiAgbXV0YXRlKHRpbWVfd2FpdGluZyA9IHRpbWUgLSBzdGFydF90aW1lKSAlPiUgDQogIHNlbGVjdChuYW1lLCB2YWx1ZSwgcmVwbGljYXRpb24sIHRpbWVfd2FpdGluZywgY29udGFjdG8pIC0+IHdhaXRpbmdfdGltZXMNCndhaXRpbmdfdGltZXMNCmBgYA0KDQoNCmBgYHtyfQ0KIyBoaXN0b2dyYW1hIChuIHRvdSBhIHBlcmNlYmVyIHBxIMOpIHEgaMOhIHdhaXRpbmcgdGltZXMgbWFpb3JlcyBxdWUgNSkNCndhaXRpbmdfdGltZXMgJT4lIA0KICBmaWx0ZXIoY29udGFjdG8gPT0gInRlbGVmb25lIikgJT4lDQogIGdncGxvdChhZXMoeCA9IHRpbWVfd2FpdGluZykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwMCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKw0KICB5bGltKDAsIDEwMDApDQoNCndhaXRpbmdfdGltZXMgJT4lIA0KICBmaWx0ZXIoY29udGFjdG8gPT0gImFwcCIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3dhaXRpbmcvNjApKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDApICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCg0KYGBge3J9DQojIHJlZ3VsYXIgd2FpdGluZyB0aW1lIHN0YXRzIHdpdGhvdXQgZ3JhcGhzDQp3YWl0aW5nX3RpbWVzICU+JSANCiAgZ3JvdXBfYnkoY29udGFjdG8pICU+JSANCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKHRpbWVfd2FpdGluZyksIHNkID0gc2QodGltZV93YWl0aW5nKSwgbWVkaWFuID0gbWVkaWFuKHRpbWVfd2FpdGluZykpIA0Kd2FpdGluZ190aW1lcyAlPiUgDQogIGdyb3VwX2J5KGNvbnRhY3RvKSAlPiUgDQogIHN1bW1hcmlzZShxdWFudGlsZV8xMCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC4xKSwgcXVhbnRpbGVfMjAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuMiksIHF1YW50aWxlXzMwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjMpLCBxdWFudGlsZV80MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC40KSwgcXVhbnRpbGVfNTAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuNSksIHF1YW50aWxlXzYwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjYpLCBxdWFudGlsZV83MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC43KSwgcXVhbnRpbGVfODAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuOCksIHF1YW50aWxlXzkwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjkpLCBxdWFudGlsZV85NSA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC45NSksIHF1YW50aWxlXzk5ID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjk5KSkNCmBgYA0KDQoNCmBgYHtyfQ0KcmVzb3VyY2VzICU+JSBwbG90KG1ldHJpYyA9ICJ1dGlsaXphdGlvbiIpDQpyZXNvdXJjZXMgJT4lIHBsb3QobWV0cmljID0gInVzYWdlIiwgc3RlcHMgPSBUKQ0KYGBgDQoNCiMjIDIuYSkgLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0NCg0KYGBge3J9DQojIGEpIHNldF9wcmlvcml0aXphdGlvbihcKCkgaWYgKGdldF9hdHRyaWJ1dGUoZW52LCAidGlwb19kZV9jb25zdWx0YSIpID09IHRpcG9fZGVfY29uc3VsdGEuVEVMRUZPTklDQSkgYygxLCAtMSwgRkFMU0UpIGVsc2UgYygwICwgLTEsIEZBTFNFKSkNCnBlc3NvYV9fIDwtIGpvaW4oDQogIHBlc3NvYVsxOjJdLCANCiAgdHJhamVjdG9yeSgiY2hhbmdlX3ByaW8iKSAlPiUgDQogICAgIHNldF9wcmlvcml0aXphdGlvbihcKCkgaWYgKGdldF9hdHRyaWJ1dGUoZW52LCAidGlwb19kZV9jb250YWN0byIpID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUpIGMoMSwgMSwgRkFMU0UpIGVsc2UgYygwICwgMCwgRkFMU0UpKSwNCiAgcGVzc29hWy0oMTozKV0NCikNCnBlc3NvYV9fDQpgYGANCg0KDQpgYGB7cn0NCmVudnMgPC0gbGFwcGx5KDE6NTAsIGZ1bmN0aW9uKGkpIHsNCiAgZW52IDw8LSBzaW1tZXIoImhvc3BpdGFsIikgDQogIGVudiAlPiUgDQogICAgYWRkX2dlbmVyYXRvcigicGVzc29hX2RlX21hbmhhIiwgcGVzc29hX18sIGZyb21fdG8oOCo2MCwgMTAqNjAsIFwoKSByZXhwKDEsICgxMjAvMikvNjApLCBldmVyeSA9IDI0KjYwLCBhcnJpdmUgPSBGKSwgbW9uID0gMikgJT4lIA0KICAgIGFkZF9nZW5lcmF0b3IoInBlc3NvYV9kZV90YXJkZSIsIHBlc3NvYV9fLCBmcm9tX3RvKDEwKjYwLCAxNio2MCwgXCgpIHJleHAoMSwgKDI0MC82KS82MCksIGV2ZXJ5ID0gMjQqNjAsIGFycml2ZSA9IEYpLCBtb24gPSAyKSAlPiUgDQogICAgYWRkX2dlbmVyYXRvcigicGVzc29hX2RlX25vaXRlIiwgcGVzc29hX18sIGZyb21fdG8oMTYqNjAsIDE4KjYwLCBcKCkgcmV4cCgxLCAoNDAvMikvNjApLCBldmVyeSA9IDI0KjYwLCBhcnJpdmUgPSBGKSwgbW9uID0gMikgJT4lIA0KICAgIGFkZF9yZXNvdXJjZSgiYWRtaW5pc3RyYWRvciIsIHNjaGVkdWxlKA0KICAgICAgYyg4ICwgOSAsIDkuNSAsIDEyICwgMTUuNSAsMTgpKjYwLCANCiAgICAgIGMoICAxICwgMiwgICAgNCwgICAzLCAgICAgMSwgIDApLCANCiAgICAgIHBlcmlvZCA9IDI0KjYwDQogICAgKSkgJT4lDQogICAgcnVuKDI0KjYwKjUpDQp9KQ0KDQplbnZzICU+JSBzaW1tZXIucGxvdDo6Z2V0X21vbl9hcnJpdmFscyhvbmdvaW5nID0gVCkgLT4gYXJyaXZhbHMNCmVudnMgJT4lIHNpbW1lci5wbG90OjpnZXRfbW9uX2F0dHJpYnV0ZXMoKSAtPiBhdHRyaWJ1dGVzDQplbnZzICU+JSBzaW1tZXIucGxvdDo6Z2V0X21vbl9yZXNvdXJjZXMoKSAtPiByZXNvdXJjZXMNCmBgYA0KDQpgYGB7cn0NCmFycml2YWxzICU+JQ0KICBmaWx0ZXIoc3RhcnRfdGltZSAhPSAtMSkgJT4lICMgbiBzZWkgcHEgZSBxIGlzdG8gYWNvbnRlY2UNCiAgYXJyYW5nZShzdGFydF90aW1lKSAlPiUgDQogIHNlbGVjdCgtYWN0aXZpdHlfdGltZSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGF5ID0gY2VpbGluZyhzdGFydF90aW1lLygyNCo2MCkpLA0KICAgIHN0YXJ0X3RpbWVfZGF5ID0gc3RhcnRfdGltZSAlJSAoMjQqNjApICU+JSBzZWNvbmRzX3RvX3BlcmlvZCgpLA0KICAgIGVuZF90aW1lX2RheSA9IGVuZF90aW1lICUlICgyNCo2MCkgJT4lIHNlY29uZHNfdG9fcGVyaW9kKCksDQogICAgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCksDQogICkgLT4gYXJyaXZhbHMuZGYyYQ0KYXJyaXZhbHMuZGYyYSAlPiUgZmlsdGVyKHJlcGxpY2F0aW9uID09IDEpICU+JSBtdXRhdGUoc3RhcnRfdGltZV9kYXkgPSBzdGFydF90aW1lX2RheSAlPiUgcm91bmQoMyksIGVuZF90aW1lX2RheSA9IGVuZF90aW1lX2RheSAlPiUgcm91bmQoMykpIA0KYGBgDQoNCmBgYHtyfQ0KYXJyaXZhbHMuZGYyYSRyZXBsaWNhdGlvbiA8LSBhcy5pbnRlZ2VyKGFzLmNoYXJhY3RlcihhcnJpdmFscy5kZjJhJHJlcGxpY2F0aW9uKSkNCg0KIyBhbmFsaXNlIGRlIGVmaWNpZW5jaWENCmF0dHJpYnV0ZXMgJT4lIA0KICBsZWZ0X2pvaW4oYXJyaXZhbHMuZGYyYSwgYnkgPSBjKCJuYW1lIiwgInJlcGxpY2F0aW9uIikgKSAlPiUgDQogIHNlbGVjdChuYW1lLCBrZXksIHZhbHVlLCByZXBsaWNhdGlvbiwgZGF5LCBlbmRfdGltZSwgZW5kX3RpbWVfZGF5KSAtPiBhdHJpYnV0ZXMuZGYyYQ0KYXRyaWJ1dGVzLmRmMmEgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpDQpgYGANCg0KYGBge3J9DQojIGFuYWxpc2UgZGUgZWZpY2llbmNpYQ0KIyBudW1lcm8gZGUgY2hhbWFkYXMgcGVyZGlkYXMNCiMgYWRkIGRheSB0byBhdHJpYnV0ZXMgZnJvbSBhcnJpdmFscy5kZjJhDQphdHRyaWJ1dGVzICU+JSANCiAgbGVmdF9qb2luKGFycml2YWxzLmRmMmEgJT4lIHNlbGVjdChuYW1lLCByZXBsaWNhdGlvbiwgZGF5LCBzdGFydF90aW1lLCBlbmRfdGltZSwgZW5kX3RpbWVfZGF5KSwgYnkgPSBjKCJuYW1lIiwgInJlcGxpY2F0aW9uIikpICU+JSANCiAgcmVuYW1lKGF0cmlidXRlX3RpbWUgPSB0aW1lLCBwZXNzb2Ffc3RhcnRfdGltZSA9IHN0YXJ0X3RpbWUsIHBlc3NvYV9lbmRfdGltZSA9IGVuZF90aW1lKSAlPiUgDQogIHNlbGVjdChhdHJpYnV0ZV90aW1lLCBuYW1lLCBrZXksIHZhbHVlLCByZXBsaWNhdGlvbiwgZGF5LCBwZXNzb2Ffc3RhcnRfdGltZSwgcGVzc29hX2VuZF90aW1lKSAtPiBhdHRyaWJ1dGVzLmRmMmENCmF0dHJpYnV0ZXMuZGYyYSANCmBgYA0KDQpgYGB7cn0NCiMgcGVzc29hcyBlIG9zIHNldXMgdGlwb3MgZGUgY2hhbWFkYQ0KYXR0cmlidXRlcy5kZjJhICU+JSANCiAgZmlsdGVyKGtleSA9PSAidGlwb19kZV9jb250YWN0byIpICU+JSANCiAgc2VsZWN0KG5hbWUsIHJlcGxpY2F0aW9uLCB2YWx1ZSkgJT4lIA0KICBtdXRhdGUodmFsdWUgPSBpZmVsc2UodmFsdWUgPT0gdGlwb19kZV9jb250YWN0by5URUxFRk9ORSwgInRlbGVmb25lIiwgImFwcCIpKSAlPiUNCiAgcmVuYW1lKGNvbnRhY3RvID0gdmFsdWUpIC0+IGNvbnRhY3Rvcy5kZjJhDQpgYGANCg0KDQpgYGB7cn0NCiMgcGVyY2VudGFnZW0gZGUgY2hhbWFkYXMgcGVyZGlkYXMNCmF0dHJpYnV0ZXMuZGYyYSAlPiUgDQogIGZpbHRlcihrZXkgPT0gInRpcG9fZGVfY29udGFjdG8iLCB2YWx1ZSA9PSB0aXBvX2RlX2NvbnRhY3RvLlRFTEVGT05FKSAlPiUgDQogIGxlZnRfam9pbihhdHRyaWJ1dGVzLmRmMmEgJT4lIGZpbHRlcihrZXkgPT0gInBob25lX3JlamVjdGVkIikgJT4lIG11dGF0ZShnYXZlX3VwID0ga2V5KSAlPiUgc2VsZWN0KG5hbWUsIHJlcGxpY2F0aW9uLCBnYXZlX3VwKSwgam9pbl9ieSgibmFtZSIsICJyZXBsaWNhdGlvbiIpKSAlPiUgDQogIG11dGF0ZShnYXZlX3VwID0gaWZfZWxzZShnYXZlX3VwICU+JSBpcy5uYSwgVCwgRiksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBzZWxlY3QobmFtZSwgcmVwbGljYXRpb24sIHBlc3NvYV9lbmRfdGltZSwgZ2F2ZV91cCkgLT4gcGhvbmVfY2FsbHMNCnBob25lX2NhbGxzDQpgYGANCg0KYGBge3J9DQojIHBob25lX2NhbGxzIHJhdGUgcGVyIHJlcGxpY2F0aW9uDQpwaG9uZV9jYWxscyAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUgDQogIHN1bW1hcmlzZShwaG9uZV9jYWxscyA9IG4oKSwgZ2F2ZV91cCA9IHN1bShnYXZlX3VwKSkgJT4lIA0KICBtdXRhdGUocGhvbmVfY2FsbHNfcGVyX2hvdXIgPSBwaG9uZV9jYWxscy8xMCwgZ2F2ZV91cF9yYXRlID0gZ2F2ZV91cC9waG9uZV9jYWxscykgJT4lIA0KICBhcnJhbmdlKGRlc2MoZ2F2ZV91cF9yYXRlKSkgLT4gcGhvbmVfY2FsbHNfcmF0ZQ0KcGhvbmVfY2FsbHNfcmF0ZQ0KDQpwaG9uZV9jYWxsc19yYXRlICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihyZXBsaWNhdGlvbiwgZ2F2ZV91cF9yYXRlKSwgeSA9IGdhdmVfdXBfcmF0ZSkpICsNCiAgZ2VvbV9jb2woKSArIA0KICB5bGltKDAsIDEpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA3MCwgaGp1c3QgPSAxKSkgKw0KICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWluKGdhdmVfdXBfcmF0ZSkpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArDQogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXgoZ2F2ZV91cF9yYXRlKSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsdWUiKSArDQogIHhsYWIoIlJlcGxpY2F0aW9uIikgKyANCiAgeWxhYigiR2F2ZSB1cCByYXRlIikNCmBgYA0KDQpgYGB7cn0NCnBob25lX2NhbGxzX3JhdGUgJT4lIA0KICBzdW1tYXJpc2UoDQogICAgbWluID0gbWluKHBob25lX2NhbGxzX3Blcl9ob3VyKSwgDQogICAgbWVhbiA9IG1lYW4ocGhvbmVfY2FsbHNfcGVyX2hvdXIpLCANCiAgICBtYXggPSBtYXgocGhvbmVfY2FsbHNfcGVyX2hvdXIpDQogICAgKQ0KYGBgDQoNCg0KYGBge3J9DQojIGV2b2x1Y2FvIGFvIGxvbmdvIGRvIGRpYSBETyBRVUU/DQphdHRyaWJ1dGVzLmRmMmEgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSANCiAgbXV0YXRlKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICByZWZyYW1lKHRpbWVfcmVqZWN0ZWQgPSBwZXNzb2FfZW5kX3RpbWUvNjAsIHN1bWN1bV9wZXJfZGF5ID0gY3Vtc3VtKHZhbHVlKSwgZGF5ID0gZGF5KSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWVfcmVqZWN0ZWQsIHkgPSBzdW1jdW1fcGVyX2RheSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KDQpgYGB7cn0NCiMgZ2V0IHF1ZXVlIHNpemUgaGlzdG9ncmFtDQpyZXNvdXJjZXMgJT4lIA0KICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZS82MCwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSkpICsNCiAgZ2VvbV9zdGVwKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCnJlc291cmNlcyAlPiUgDQogIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLzYwLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSkgKw0KICBnZW9tX3N0ZXAoYWVzKGNvbG9yID0gcmVwbGljYXRpb24pLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICB5bGltKDAsMjUwKQ0KICANCmBgYA0KDQoNCmBgYHtyfQ0KIyBqb2luIHRoZXNlIDIgcGxvdHMNCmF0dHJpYnV0ZXMuZGYyYSAlPiUgDQogIGZpbHRlcihrZXkgPT0gInBob25lX3JlamVjdGVkIikgJT4lIA0KICBtdXRhdGUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKSAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUgDQogIHJlZnJhbWUodGltZV9yZWplY3RlZCA9IHBlc3NvYV9lbmRfdGltZS82MCwgc3VtY3VtX3Blcl9kYXkgPSBjdW1zdW0odmFsdWUpLCBkYXkgPSBkYXkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdGltZV9yZWplY3RlZCwgeSA9IHN1bWN1bV9wZXJfZGF5KSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gcmVwbGljYXRpb24pLCBhbHBoYSA9IDAuMykgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBnZW9tX3N0ZXAoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgICAgICAgICAgICAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLzYwLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSwgDQogICAgICAgICAgICBhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSwgYWxwaGEgPSAwLjMpICsgDQogIGdlb21fc21vb3RoKGRhdGEgPSByZXNvdXJjZXMgJT4lIA0KICAgICAgICAgICAgICAgIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZS82MCwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSwgDQogICAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBtZXRob2Q9ImxtIikgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIHlsaW0oMCw4NzUpDQogIA0KYXR0cmlidXRlcy5kZjJhICU+JSANCiAgZmlsdGVyKGtleSA9PSAicGhvbmVfcmVqZWN0ZWQiKSAlPiUgDQogIG11dGF0ZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JSANCiAgcmVmcmFtZSh0aW1lX3JlamVjdGVkID0gcGVzc29hX2VuZF90aW1lLzYwLCBzdW1jdW1fcGVyX2RheSA9IGN1bXN1bSh2YWx1ZSksIGRheSA9IGRheSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3JlamVjdGVkLCB5ID0gc3VtY3VtX3Blcl9kYXkpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArDQogIGdlb21fc21vb3RoKCkgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGdlb21fc3RlcChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUvNjAsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLzYwLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIG1ldGhvZD0ibG0iKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgZ2d0aXRsZSgiVGFtYW5obyBkYSBmaWxhIGUgY2hhbWFkYXMgcmVqZWl0YWRhcyAyYSkgcG9yIGRpYSIpICsNCiAgeGxhYigiVGVtcG8gZGUgc2ltdWxhw6fDo28gKGhvcmFzKSIpICsgDQogIHlsYWIoIk7Dum1lcm8gZGUgY2hhbWFkYXMgcmVqZWl0YWRhcyIpDQoNCg0KDQphdHRyaWJ1dGVzLmRmMmEgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSANCiAgbXV0YXRlKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICByZWZyYW1lKHRpbWVfcmVqZWN0ZWQgPSBwZXNzb2FfZW5kX3RpbWUvNjAsIHN1bWN1bV9wZXJfZGF5ID0gY3Vtc3VtKHZhbHVlKSwgZGF5ID0gZGF5KSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWVfcmVqZWN0ZWQsIHkgPSBzdW1jdW1fcGVyX2RheSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQoNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGdlb21fc3RlcChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUvNjAsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLzYwLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIG1ldGhvZD0ibG0iKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBnZ3RpdGxlKCJUYW1hbmhvIGRhIGZpbGEgZSBjaGFtYWRhcyByZWplaXRhZGFzIDJhKSBub3MgNSBkaWFzIikgKw0KICB4bGFiKCJUZW1wbyBkZSBzaW11bGHDp8OjbyAoaG9yYXMpIikgKyANCiAgeWxhYigiTsO6bWVybyBkZSBjaGFtYWRhcyByZWplaXRhZGFzIikNCg0KYGBgDQoNCmBgYHtyfQ0KIyB0ZW1wbyBkZSBlc3BlcmEgZGFxdWVsZXMgcXVlIGNvbmNsdWlyYW0sIHNwZWFyZGFvIHBvciBjaGFtYWRhIGUgbWVuc2FnZW0NCmF0dHJpYnV0ZXMgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJhdGVuZGlkbyIpICU+JSANCiAgbGVmdF9qb2luKGFycml2YWxzLmRmMmEgJT4lIHNlbGVjdChzdGFydF90aW1lLCBuYW1lLCByZXBsaWNhdGlvbiksIGpvaW5fYnkoIm5hbWUiLCAicmVwbGljYXRpb24iKSkgJT4lIA0KICBsZWZ0X2pvaW4oY29udGFjdG9zLmRmMmEsIGpvaW5fYnkoIm5hbWUiLCAicmVwbGljYXRpb24iKSkgJT4lIA0KICBtdXRhdGUodGltZV93YWl0aW5nID0gdGltZSAtIHN0YXJ0X3RpbWUpICU+JSANCiAgc2VsZWN0KG5hbWUsIHZhbHVlLCByZXBsaWNhdGlvbiwgdGltZV93YWl0aW5nLCBjb250YWN0bykgLT4gd2FpdGluZ190aW1lcw0Kd2FpdGluZ190aW1lcw0KYGBgDQoNCg0KYGBge3J9DQojIGhpc3RvZ3JhbWEgKG4gdG91IGEgcGVyY2ViZXIgcHEgw6kgcSBow6Egd2FpdGluZyB0aW1lcyBtYWlvcmVzIHF1ZSA1KQ0Kd2FpdGluZ190aW1lcyAlPiUgDQogIGZpbHRlcihjb250YWN0byA9PSAidGVsZWZvbmUiKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gdGltZV93YWl0aW5nKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrIA0KICB5bGltKDAsIDEwMDApDQoNCndhaXRpbmdfdGltZXMgJT4lIA0KICBmaWx0ZXIoY29udGFjdG8gPT0gImFwcCIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3dhaXRpbmcvNjApKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDApICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCg0KYGBge3J9DQojIHJlZ3VsYXIgd2FpdGluZyB0aW1lIHN0YXRzIHdpdGhvdXQgZ3JhcGhzDQp3YWl0aW5nX3RpbWVzICU+JSANCiAgZ3JvdXBfYnkoY29udGFjdG8pICU+JSANCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKHRpbWVfd2FpdGluZyksIHNkID0gc2QodGltZV93YWl0aW5nKSwgbWVkaWFuID0gbWVkaWFuKHRpbWVfd2FpdGluZykpIA0Kd2FpdGluZ190aW1lcyAlPiUgDQogIGdyb3VwX2J5KGNvbnRhY3RvKSAlPiUgDQogIHN1bW1hcmlzZShxdWFudGlsZV8xMCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC4xKSwgcXVhbnRpbGVfMjAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuMiksIHF1YW50aWxlXzMwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjMpLCBxdWFudGlsZV80MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC40KSwgcXVhbnRpbGVfNTAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuNSksIHF1YW50aWxlXzYwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjYpLCBxdWFudGlsZV83MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC43KSwgcXVhbnRpbGVfODAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuOCksIHF1YW50aWxlXzkwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjkpLCBxdWFudGlsZV85NSA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC45NSksIHF1YW50aWxlXzk5ID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjk5KSkNCmBgYA0KDQoNCmBgYHtyfQ0KcmVzb3VyY2VzICU+JSBwbG90KG1ldHJpYyA9ICJ1dGlsaXphdGlvbiIpDQpyZXNvdXJjZXMgJT4lIHBsb3QobWV0cmljID0gInVzYWdlIiwgc3RlcHMgPSBUKQ0KYGBgDQoNCg==